diff options
Diffstat (limited to 'csys')
-rw-r--r-- | csys/README | 3 | ||||
-rw-r--r-- | csys/cache-structs.scm | 48 | ||||
-rw-r--r-- | csys/compiler-driver.scm | 640 | ||||
-rw-r--r-- | csys/csys.scm | 25 | ||||
-rw-r--r-- | csys/dump-cse.scm | 182 | ||||
-rw-r--r-- | csys/dump-flic.scm | 130 | ||||
-rw-r--r-- | csys/dump-interface.scm | 800 | ||||
-rw-r--r-- | csys/dump-macros.scm | 37 | ||||
-rw-r--r-- | csys/dump-params.scm | 18 | ||||
-rw-r--r-- | csys/magic.scm | 10 |
10 files changed, 1893 insertions, 0 deletions
diff --git a/csys/README b/csys/README new file mode 100644 index 0000000..6f9c183 --- /dev/null +++ b/csys/README @@ -0,0 +1,3 @@ +This directory contains everything relating to the compilation system, +including stuff for parsing unit files, incremental recompilation, and +reading and writing code and interface files. diff --git a/csys/cache-structs.scm b/csys/cache-structs.scm new file mode 100644 index 0000000..ba38840 --- /dev/null +++ b/csys/cache-structs.scm @@ -0,0 +1,48 @@ +;;; these structures deal with the compilation system and the unit cache. + +;;; An entry in the unit cache: + +(define-struct ucache + (slots + (ufile (type string)) ; the name of the file containing the unit definition + (cifile (type string)) ; the filename of the (compiled) interface file + (sifile (type string)) ; the filename of the (uncompiled) interface file + (cfile (type string)) ; the filename of the (compiled) output file + (sfile (type string)) ; the filename of the (uncompiled) output file + (udate (type integer)) ; the write date of ufile + (idate (type integer)) ; the time stamp of the binary interface file + (stable? (type bool)) ; the stable flag + (load-prelude? (type bool)) ; true if unit uses standard prelude + ;; status is initially available (in cache). It is set to loading when + ;; requested and loaded once all imported units are loaded. + (status (type (enum loaded loading available))) + (ifile-loaded (type bool)) ; true when interface is loaded (modules) + (code-loaded (type bool)) ; true when the associated code is in memory + (source-files (type (list string))) ; source files in the unit + (imported-units (type (list string))) ; the filenames of imported unit files + (lisp-files (type (list (tuple string string)))) ; source/binary pairs + (modules (type (list module))) + (printers-set? (type bool)) + (printers (type (list symbol))) + (optimizers-set? (type bool)) + (optimizers (type (list symbol))) + (chunk-size (type (maybe int))) + )) + + +;;; This is used to hold various flags used by the compilation system, +;;; instead of passing them all as individual arguments. + +(define-struct cflags + (slots + ;; Whether to load code for unit into core + (load-code? (type bool) (default '#t)) + ;; Whether to create an output code file. + (write-code? (type bool) (default '#t)) + ;; Affects whether write-code? creates a source or compiled file, + ;; and whether load-code? uses the interpreter or compiler. + ;; Ignored if load-code? and write-code? are both false. + (compile-code? (type bool) (default '#t)) + ;; Whether to create an output interface file. + (write-interface? (type bool) (default '#t)) + )) diff --git a/csys/compiler-driver.scm b/csys/compiler-driver.scm new file mode 100644 index 0000000..5ce5a71 --- /dev/null +++ b/csys/compiler-driver.scm @@ -0,0 +1,640 @@ +;;; compiler-driver.scm -- compilation unit management +;;; +;;; author : John & Sandra +;;; +;;; + + +;;; Flags for controlling various low-level behaviors of the compiler. +;;; You might want to tweak these in the system-building scripts for +;;; different Lisps, but users don't normally need to mess with them. + +(define *compile-interface* '#f) +(define *interface-code-quality* 2) +(define *interface-chunk-size* '#f) +(define *default-code-quality* 2) +(define *optimized-code-quality* 3) +(define *code-chunk-size* 300) + + + +;;;===================================================================== +;;; Main entry point +;;;===================================================================== + +;;; This is the top level driver for the compiler. It takes a file name +;;; and output controls. It returns '#f if compilation fails. + +(define *codefile-cache* '()) + +(define (haskell-compile filename cflags) + (initialize-haskell-system) + (let/cc abort-compile + (dynamic-let ((*abort-compilation* + (lambda () (funcall abort-compile '#f)))) + (initialize-compilation) + (let ((unit (find-cunit-name filename))) + (let ((res (load-compilation-unit unit cflags))) + (map (lambda (x) (module-name x)) (ucache-modules res))))))) + +;;; this is the initialization code that occurs at the start of compilation. + +(define (initialize-compilation) + (initialize-module-table) + (for-each-unit + (lambda (u) + (setf (ucache-status u) 'available)))) + + + +;;;===================================================================== +;;; Filename utilities +;;;===================================================================== + +;;; File extensions + +(define *source-file-extensions* '(".hs" ".lhs")) +(define *unit-file-extension* ".hu") +(define *interface-file-extension* ".hi") +(define *lisp-file-extensions* '(".lisp" ".scm")) + +(define (source-extension? x) + (mem-string x *source-file-extensions*)) + +(define (unit-extension? x) + (string=? x *unit-file-extension*)) + +(define (interface-extension? x) + (string=? x *interface-file-extension*)) + +(define (lisp-extension? x) + (mem-string x *lisp-file-extensions*)) + + +;;; Build file names. + +(define (make-cifilename filename) + (let ((place (filename-place filename)) + (name (string-append (filename-name filename) "-hci"))) + (assemble-filename place name binary-file-type))) + +(define (make-sifilename filename) + (let ((place (filename-place filename)) + (name (string-append (filename-name filename) "-hci"))) + (assemble-filename place name source-file-type))) + +(define (make-cfilename filename) + (add-extension filename binary-file-type)) + +(define (make-sfilename filename) + (add-extension filename source-file-type)) + + +;;; This take a file name (extension ignored) & searches for a unit file. + +(define (locate-existing-cunit name) + (locate-extension name (list *unit-file-extension*))) + +;;; This take a file name (extension ignored) & searches for a source file. + +(define (locate-existing-source-file name) + (locate-extension name *source-file-extensions*)) + +(define (locate-extension name extensions) + (if (null? extensions) + '#f + (let ((name-1 (add-extension name (car extensions)))) + (if (file-exists? name-1) + name-1 + (locate-extension name (cdr extensions)))))) + + +;;; This delivers the name of a compilation unit. The extension of the name +;;; is ignored & a test for the presence of a compilation unit with +;;; the same name is done. If none is found, signal an error. + +(define (find-cunit-name name) + (or (locate-existing-cunit name) + (locate-existing-source-file name) + (signal-file-not-found name))) + + + +;;;===================================================================== +;;; Compilation unit file parsing +;;;===================================================================== + +;;; This parses a unit file. The file simply contains a list of file names. +;;; The files are sorted into two catagories: other compilation units and +;;; source files in the current unit. When a file has no extension, the system +;;; checks for a unit file first and then a source file. + +(define (parse-compilation-unit filename) + (let ((unit-type (filename-type filename))) + (if (or (source-extension? unit-type) (interface-extension? unit-type)) + (create-ucache filename filename (list filename) '() '() '#f '#t + '#f '() '#f '() '#f) + (parse-compilation-unit-aux + filename + (call-with-input-file filename (function gather-file-names)))))) + +(define (create-ucache filename output-filename + source-files imports lisp-files + stable? load-prelude? + printers-set? printers optimizers-set? optimizers + chunk-size) + (let* ((cifilename + (make-cifilename output-filename)) + (sifilename + (make-sifilename output-filename)) + (all-imports + (if load-prelude? + (cons *prelude-unit-filename* imports) + imports)) + (cache-entry + (make ucache + (ufile filename) + (sifile sifilename) + (cifile cifilename) + (sfile (make-sfilename output-filename)) + (cfile (make-cfilename output-filename)) + (udate (current-date)) + (idate (get-latest-ifiledate cifilename sifilename)) + (stable? stable?) + (load-prelude? load-prelude?) + (status 'loading) + (ifile-loaded '#f) + (code-loaded '#f) + (source-files source-files) + (imported-units all-imports) + (lisp-files lisp-files) + (modules '()) + (printers-set? printers-set?) + (printers printers) + (optimizers-set? optimizers-set?) + (optimizers optimizers) + (chunk-size chunk-size)))) + (install-compilation-unit filename cache-entry) + cache-entry)) + +(define (get-latest-ifiledate cifilename sifilename) + (max (or (and (file-exists? cifilename) + (file-write-date cifilename)) + 0) + (or (and (file-exists? sifilename) + (file-write-date sifilename)) + 0))) + + +;;; This returns a list of strings. Blank lines and lines starting in - +;;; are ignored. + +(define (gather-file-names port) + (let ((char (peek-char port))) + (cond ((eof-object? char) + '()) + ((or (char=? char '#\newline) (char=? char '#\-)) + (read-line port) + (gather-file-names port)) + (else + (let ((line (read-line port))) + (cons line (gather-file-names port))))))) + + +;;; Actually parse contents of the unit file. + +;;; These are in the command-interface stuff. +(predefine (set-printers args mode)) +(predefine (set-optimizers args mode)) +(predefine (parse-command-args string start next end)) + +(define (parse-compilation-unit-aux filename strings) + (let ((input-defaults filename) + (output-defaults filename) + (import-defaults filename) + (stable? '#f) + (load-prelude? '#t) + (filenames '()) + (imports '()) + (sources '()) + (lisp-files '()) + (printers '()) + (printers-set? '#f) + (optimizers '()) + (optimizers-set? '#f) + (chunk-size '#f) + (temp '#f)) + ;;; First look for magic flags. + (dolist (s strings) + (cond ((setf temp (string-match-prefix ":input" s)) + (setf input-defaults (merge-file-defaults temp filename))) + ((setf temp (string-match-prefix ":output" s)) + (setf output-defaults (merge-file-defaults temp filename))) + ((setf temp (string-match-prefix ":import" s)) + (setf import-defaults (merge-file-defaults temp filename))) + ((string=? ":stable" s) + (setf stable? '#t)) + ((string=? ":prelude" s) + (setf load-prelude? '#f)) + ((setf temp (string-match-prefix ":p=" s)) + (setf printers-set? '#t) + (setf printers + (set-printers + (parse-command-args temp 0 0 (string-length temp)) + '=))) + ((setf temp (string-match-prefix ":o=" s)) + (setf optimizers-set? '#t) + (setf optimizers + (set-optimizers + (parse-command-args temp 0 0 (string-length temp)) + '=))) + ((setf temp (string-match-prefix ":chunk-size" s)) + (setf chunk-size (string->number temp))) + (else + (push s filenames)))) + ;;; Next sort filenames into imports and source files. + (dolist (s filenames) + (let ((type (filename-type s)) + (fname '#f)) + (cond ((string=? type "") ; punt for now on this issue + (signal-extension-needed s)) +; ((cond ((setf fname +; (locate-existing-cunit +; (merge-file-defaults s import-defaults))) +; (push fname imports)) +; ((setf fname +; (locate-existing-source-file +; (merge-file-defaults s input-defaults))) +; (push fname sources)) +; (else +; (signal-unit-not-found s)))) + ((unit-extension? type) + (setf fname (merge-file-defaults s import-defaults)) + (if (file-exists? fname) + (push fname imports) + (signal-unit-not-found fname))) + ((or (source-extension? type) (interface-extension? type)) + (setf fname (merge-file-defaults s input-defaults)) + (if (file-exists? fname) + (push fname sources) + (signal-unit-not-found fname))) + ((lisp-extension? type) + (setf fname (merge-file-defaults s input-defaults)) + (if (file-exists? fname) + (push (cons fname + (add-extension + (merge-file-defaults s output-defaults) + binary-file-type)) + lisp-files) + (signal-unit-not-found fname))) + (else + (signal-unknown-file-type s))))) + ;; Finally create the unit object. + (create-ucache filename output-defaults + sources imports lisp-files + stable? load-prelude? + printers-set? printers optimizers-set? optimizers + chunk-size))) + + +;;; Helper functions for the above. + +(define (string-match-prefix prefix s) + (let ((prefix-length (string-length prefix)) + (s-length (string-length s))) + (if (>= s-length prefix-length) + (string-match-prefix-aux prefix s prefix-length s-length 0) + '#f))) + +(define (string-match-prefix-aux prefix s prefix-length s-length i) + (cond ((eqv? i prefix-length) + (string-match-prefix-aux-aux s s-length i)) + ((not (char=? (string-ref s i) (string-ref prefix i))) + '#f) + (else + (string-match-prefix-aux prefix s prefix-length s-length (1+ i))))) + +(define (string-match-prefix-aux-aux s s-length i) + (cond ((eqv? i s-length) + "") + ((let ((ch (string-ref s i))) + (or (char=? ch '#\space) (char=? ch #\tab))) + (string-match-prefix-aux-aux s s-length (1+ i))) + (else + (substring s i s-length)))) + +(define (merge-file-defaults filename defaults) + (let ((place (filename-place filename)) + (name (filename-name filename)) + (type (filename-type filename))) + (assemble-filename + (if (string=? place "") defaults place) + (if (string=? name "") defaults name) + (if (string=? type "") defaults type)))) + + +;;;===================================================================== +;;; Guts +;;;===================================================================== + + +;;; This is the main entry to the compilation system. This causes a +;;; unit to be compiled and/or loaded. + +(define (load-compilation-unit filename cflags) + (let ((cunit (lookup-compilation-unit filename))) + (cond ((eq? cunit '#f) + ;; Unit not found in cache. + (load-compilation-unit-aux + (parse-compilation-unit filename) cflags)) + ((eq? (ucache-status cunit) 'loaded) + ;; Already loaded earlier in this compile. + cunit) + ((eq? (ucache-status cunit) 'loading) + (signal-circular-unit filename)) + (else + (load-compilation-unit-aux cunit cflags)) + ))) + + +(define (load-compilation-unit-aux c cflags) + (setf (ucache-status c) 'loading) + (load-imported-units c cflags) + (if (unit-valid? c cflags) + (load-compiled-unit c (cflags-load-code? cflags)) + (locally-compile c cflags)) + (setf (ucache-status c) 'loaded) + ;; Hack, hack. When loading the prelude, make sure magic symbol + ;; table stuff is initialized. + (when (string=? (ucache-ufile c) *prelude-unit-filename*) + (init-prelude-globals)) + c) + +(define (load-compiled-unit c load-code?) + (when (and load-code? (not (ucache-code-loaded c))) + (when (memq 'loading *printers*) + (format '#t "~&Loading unit ~s.~%" (ucache-ufile c)) + (force-output)) + (load-lisp-files (ucache-lisp-files c)) + (load-more-recent-file (ucache-cfile c) (ucache-sfile c)) + (setf (ucache-code-loaded c) '#t)) + (when (not (ucache-ifile-loaded c)) + (read-binary-interface c)) + (dolist (m (ucache-modules c)) + (add-module-to-symbol-table m)) + (link-instances (ucache-modules c))) + + +;;; These globals save the Prelude symbol table to avoid copying it +;;; into all modules which use the Prelude. + +;;; Danger! This assumes that every local symbol in the Prelude is +;;; exported. + +(define *prelude-initialized* '#f) + +(define (init-prelude-globals) + (when (not *prelude-initialized*) + (let ((pmod (locate-module '|Prelude|))) + (setf *prelude-symbol-table* (module-symbol-table pmod)) + (setf *prelude-fixity-table* (module-fixity-table pmod)) + (when (eq? (module-inverted-symbol-table pmod) '#f) + (let ((table (make-table))) + (table-for-each (lambda (name def) + (setf (table-entry table def) name)) + *prelude-symbol-table*) + (setf (module-inverted-symbol-table pmod) table))) + (setf *prelude-inverted-symbol-table* + (module-inverted-symbol-table pmod))) + (setf *prelude-initialized* '#t))) + + +;;; This recursively loads all units imported by a given unit. + +(define (load-imported-units c cflags) + (dolist (filename (ucache-imported-units c)) + (load-compilation-unit filename cflags))) + + + +;;; Load or compile lisp files. + +(define (load-lisp-files lisp-files) + (dolist (f lisp-files) + (load-more-recent-file (cdr f) (car f)))) + +(define (compile-lisp-files lisp-files) + (dolist (f lisp-files) + (let ((source (car f)) + (binary (cdr f))) + (when (not (lisp-binary-current source binary)) + (compile-file source binary)) + (load binary)))) + + + +;;; This determines whether a unit is valid. + +(define (unit-valid? c cflags) + (and (or (ucache-stable? c) + ;; If the unit is not stable, make sure its source files + ;; haven't changed. + (and (all-imports-current (ucache-imported-units c) + (ucache-idate c)) + (all-sources-current (ucache-source-files c) + (ucache-idate c)) + (all-lisp-sources-current (ucache-lisp-files c) + (ucache-idate c)))) + (or (ucache-ifile-loaded c) + ;; If the interface hasn't been loaded already, make sure + ;; that the interface file exists. + (file-exists? (ucache-cifile c)) + (file-exists? (ucache-sifile c))) + (or (not (cflags-load-code? cflags)) + ;; If we're going to load code, make sure that the code file + ;; exists. + (ucache-code-loaded c) + (file-exists? (ucache-cfile c)) + (file-exists? (ucache-sfile c))) + (or (not (cflags-write-code? cflags)) + ;; If we need to produce a code file, make sure this has + ;; already been done. + ;; Don't write files for stable units which have already + ;; been loaded, regardless of whether or not the file exists. + (and (ucache-stable? c) (ucache-code-loaded c)) + (file-exists? (ucache-cfile c)) + (and (not (cflags-compile-code? cflags)) + (file-exists? (ucache-sfile c)))) + (or (not (cflags-compile-code? cflags)) + ;; If we need to compile the lisp files, make sure this has + ;; already been done. + ;; Don't do this for stable units which have already + ;; been loaded. + (and (ucache-stable? c) (ucache-code-loaded c)) + (all-lisp-binaries-current (ucache-lisp-files c))) + (or (not (cflags-write-interface? cflags)) + ;; If we need to produce an interface file, make sure this has + ;; already been done. + ;; Don't write files for stable units which have already + ;; been loaded, regardless of whether or not the file exists. + (and (ucache-stable? c) (ucache-ifile-loaded c)) + (file-exists? (ucache-cifile c)) + (and (not *compile-interface*) + (file-exists? (ucache-sifile c)))) + )) + +(define (all-sources-current sources unit-write-date) + (every (lambda (s) + (let ((d (file-write-date s))) + (and d (> unit-write-date d)))) + sources)) + +(define (all-imports-current imports unit-write-date) + (every (lambda (s) (> unit-write-date + (ucache-idate (lookup-compilation-unit s)))) + imports)) + +(define (all-lisp-sources-current lisp-files unit-write-date) + (every (lambda (s) + (let ((d (file-write-date (car s)))) + (and d (> unit-write-date d)))) + lisp-files)) + +(define (all-lisp-binaries-current lisp-files) + (every (lambda (s) + (lisp-binary-current (car s) (cdr s))) + lisp-files)) + +(define (lisp-binary-current source binary) + (and (file-exists? binary) + (let ((sd (file-write-date source)) + (bd (file-write-date binary))) + (and sd bd (> bd sd))))) + + +;;; This does the actual job of compilation. + +(define (locally-compile c cflags) + (dynamic-let ((*printers* + (if (ucache-printers-set? c) + (ucache-printers c) + (dynamic *printers*))) + (*optimizers* + (if (ucache-optimizers-set? c) + (ucache-optimizers c) + (dynamic *optimizers*)))) + (when (memq 'compiling *printers*) + (format '#t "~&Compiling unit ~s.~%Optimizers: ~A~%" + (ucache-ufile c) + *optimizers*) + (force-output)) + (if (cflags-compile-code? cflags) + (compile-lisp-files (ucache-lisp-files c)) + (load-lisp-files (ucache-lisp-files c))) + (multiple-value-bind (mods code) + (compile-haskell-files (ucache-source-files c)) + ;; General bookkeeping to update module interface in cache. + (setf (ucache-modules c) mods) + (setf (ucache-idate c) (current-date)) + (setf (ucache-ifile-loaded c) '#t) + ;; Write interface file if necessary. + (when (cflags-write-interface? cflags) + (let ((phase-start-time (get-run-time)) + (icode (create-dump-code c mods (ucache-load-prelude? c)))) + (if (dynamic *compile-interface*) + (write-compiled-code-file + (ucache-cifile c) + icode + (dynamic *interface-code-quality*) + (dynamic *interface-chunk-size*)) + (write-interpreted-code-file (ucache-sifile c) icode '#f)) + (when (memq 'phase-time *printers*) + (let* ((current-time (get-run-time)) + (elapsed-time (- current-time phase-start-time))) + (format '#t "Interface complete: ~A seconds~%" elapsed-time) + (force-output))))) + ;; Write code file if necessary. + (when (cflags-write-code? cflags) + (if (cflags-compile-code? cflags) + (write-compiled-code-file + (ucache-cfile c) + code + (if (memq 'lisp (dynamic *optimizers*)) + (dynamic *optimized-code-quality*) + (dynamic *default-code-quality*)) + (or (ucache-chunk-size c) (dynamic *code-chunk-size*))) + (write-interpreted-code-file (ucache-sfile c) code '#t))) + ;; Load or evaluate code if necessary. + ;; If we just wrote a compiled code file, load that; otherwise + ;; do eval or in-core compilation. + (when (cflags-load-code? cflags) + (if (and (cflags-write-code? cflags) + (cflags-compile-code? cflags)) + (load (ucache-cfile c)) + (eval code (cflags-compile-code? cflags))) + (setf (ucache-code-loaded c) '#t)) + ))) + + + +;;;===================================================================== +;;; Cache manager +;;;===================================================================== + +;;; This is the cache manager for compilation units. We use an alist at +;;; the moment. + +(define *unit-cache* '()) + +(define (reset-unit-cache) + (setf *unit-cache* '())) + + +;;; This checks to make sure that the compilation unit it finds +;;; in the cache has not been made out-of-date by updates to the unit file. + +(define (lookup-compilation-unit name) + (let ((r (ass-string name *unit-cache*))) + (if r + (let ((c (cdr r))) + (if (or (ucache-stable? c) + (> (ucache-udate c) + (or (file-write-date (ucache-ufile c)) 0))) + c + '#f)) + '#f))) + +(define (install-compilation-unit name c) + (let ((r (ass-string name *unit-cache*))) + (if (eq? r '#f) + (push (cons name c) *unit-cache*) + (setf (cdr r) c)))) + +(define (for-each-unit proc) + (dolist (c *unit-cache*) + (funcall proc (cdr c)))) + + +;;;===================================================================== +;;; Error utilities +;;;===================================================================== + +(define (signal-circular-unit filename) + (fatal-error 'circular-unit + "The compilation unit ~a has a circular dependency." + filename)) + +(define (signal-unit-not-found filename) + (fatal-error 'unit-not-found + "The compilation unit file ~a was not found." + filename)) + +(define (signal-extension-needed filename) + (fatal-error 'extension-needed + "You must provide an extension on the filename ~a in the .hu file." + filename)) + + + + + diff --git a/csys/csys.scm b/csys/csys.scm new file mode 100644 index 0000000..a4683b6 --- /dev/null +++ b/csys/csys.scm @@ -0,0 +1,25 @@ +;;; csys.scm -- compilation unit definition for the compilation system + +(define-compilation-unit csys + (source-filename "$Y2/csys/") + (require global runtime flic) + (unit cache-structs + (source-filename "cache-structs.scm")) + (unit compiler-driver + (require cache-structs) + (source-filename "compiler-driver.scm")) + (unit dump-params + (require cache-structs) + (source-filename "dump-params.scm")) + (unit dump-macros + (require dump-params) + (source-filename "dump-macros.scm")) + (unit dump-interface + (require dump-macros) + (source-filename "dump-interface.scm")) + (unit dump-flic + (require dump-macros) + (source-filename "dump-flic.scm")) + (unit dump-cse + (require dump-macros) + (source-filename "dump-cse.scm"))) diff --git a/csys/dump-cse.scm b/csys/dump-cse.scm new file mode 100644 index 0000000..38ec020 --- /dev/null +++ b/csys/dump-cse.scm @@ -0,0 +1,182 @@ +;;; This file handles common subexpressions in the interface file. +;;; Common subexpressions are detected in two places: gtypes and strictness +;;; properties. + +;;; Compressing strictness signatures + +;;; A strictness is represented by a list of booleans. We do two things to +;;; compress strictnesses: all lists less than *pre-defined-strictness-size* +;;; are pre-computed in a vector and the first *pre-defined-strictness-vars* +;;; vector elements are cached in global vars. The strictness will dump as +;;; as either a global or as a vector reference into the vector. + +(define (initialize-strictness-table) + (setf (dynamic *pre-defined-strictness-table*) + (make-vector (expt 2 (1+ (dynamic *pre-defined-strictness-size*))))) + (setf (vector-ref *pre-defined-strictness-table* 1) '()) + (do ((i 1 (1+ i)) + (j 1 (* j 2)) + (k 2 (* k 2))) + ((> i *pre-defined-strictness-size*)) + (do ((l 0 (1+ l))) + ((>= l j)) + (setf (vector-ref *pre-defined-strictness-table* (+ k l)) + (cons '#f (vector-ref *pre-defined-strictness-table* (+ j l)))) + (setf (vector-ref *pre-defined-strictness-table* (+ k j l)) + (cons '#t (vector-ref *pre-defined-strictness-table* (+ j l)))))) + (set-strictness-vars)) + +(define (strictness-table-ref x) + (vector-ref (dynamic *pre-defined-strictness-table*) x)) + +(define (dump-strictness s) + (if (null? s) + ''() + (dump-strictness-1 s s 0 0))) + +(define (dump-strictness-1 s s1 n size) + (if (null? s1) + (if (> size *pre-defined-strictness-size*) + (dump-big-strictness (- size *pre-defined-strictness-size*) s) + (let ((k (+ n (expt 2 size)))) + (if (< k *pre-defined-strictness-vars*) + `(dynamic ,(vector-ref *pre-defined-strictness-names* k)) + `(strictness-table-ref ,k)))) + (dump-strictness-1 s (cdr s1) (+ (* 2 n) (if (car s1) 1 0)) (1+ size)))) + +(define (dump-big-strictness k s) + (if (= k 0) + (dump-strictness s) + `(cons ',(car s) + ,(dump-big-strictness (1- k) (cdr s))))) + +;;; This routine handles saving type signatures (gtypes). +;;; common subexpressions are detected in two places: the type body +;;; and the the contexts. + +(define (init-predefined-gtyvars) + (setf *saved-gtyvars* (make-vector *num-saved-gtyvars*)) + (dotimes (i *num-saved-gtyvars*) + (setf (vector-ref *saved-gtyvars* i) (**gtyvar i))) + (setup-gtyvar-vars)) + +(define (init-cse-structs) + (initialize-strictness-table) + (init-predefined-gtyvars)) + +(define (save-cse-value v) + (setf (vector-ref (dynamic *saved-cse-values*) (dynamic *cse-value-num*)) v) + (incf (dynamic *cse-value-num*))) + +(define (cse-init-code) + (let* ((n (length *cse-objects*)) + (init-code '())) + (do ((i (1- n) (1- i)) + (init *cse-objects* (cdr init))) + ((null? init)) + (push `(save-cse-value ,(car init)) init-code)) + `((setf *saved-cse-values* (make-vector ,n)) + (setf *cse-value-num* 0) + ,@init-code))) + +(define (remember-dumped-object init-code) + (push init-code *cse-objects*) + (incf *cse-object-num*) + *cse-object-num*) + +(define (cse-value-ref x) + (vector-ref (dynamic *saved-cse-values*) x)) + +(define (cse-ref-code n) + (cond ((eqv? n 0) + ''()) + ((<= n *num-saved-gtyvars*) + `(dynamic ,(vector-ref *saved-gtyvar-varnames* (1- n)))) + (else + `(cse-value-ref ,(- n *num-saved-gtyvars* 1))))) + +(define (dump-gtyvar g) + (let ((n (gtyvar-varnum g))) + (if (< n *num-saved-gtyvars*) + (1+ n) + (remember-dumped-object `(**gtyvar ,n))))) + +(define (dump-context-list contexts) + (if (null? contexts) + 0 + (let* ((rest (dump-context-list (cdr contexts))) + (classes (dump-class-list (car contexts))) + (t1 (assq/insert-l classes *gtype-class-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,(cse-ref-code classes) ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-class-list classes) + (if (null? classes) + 0 + (let* ((rest (dump-class-list (cdr classes))) + (class (dump-class/n (car classes))) + (t1 (assq/insert-l class *context-class-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,class ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-gtype-1 g) + (cond ((gtyvar? g) + (dump-gtyvar g)) + ((ntyvar? g) + (dump-gtype-1 (prune g))) + (else + (dump-gtycon g)))) + +(define (dump-gtycon g) + (let* ((ty (ntycon-tycon g)) + (tycon (if (algdata? ty) (dump-algdata/n ty) (dump-synonym/n ty))) + (l (dump-gtype-list (ntycon-args g))) + (t1 (assq/insert-l tycon *gtype-tycon-index*)) + (res (assq/insert l (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(**ntycon ,tycon ,(cse-ref-code l))))) + (setf (cdr res) z) + z) + (cdr res)))) + +(define (dump-gtype-list l) + (if (null? l) + 0 + (let* ((g (dump-gtype-1 (car l))) + (rest (dump-gtype-list (cdr l))) + (t1 (assq/insert-l g *gtype-list-index*)) + (res (assq/insert rest (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(cons ,(cse-ref-code g) + ,(cse-ref-code rest))))) + (setf (cdr res) z) + z) + (cdr res))))) + +(define (dump-gtype/cse g) + (cse-ref-code + (let* ((context (dump-context-list (gtype-context g))) + (type (dump-gtype-1 (gtype-type g))) + (t1 (assq/insert-l type *gtype-index*)) + (res (assq/insert context (cdr t1)))) + (if (eq? (cdr res) '#f) + (let ((z (remember-dumped-object + `(**gtype ,(cse-ref-code context) + ,(cse-ref-code type))))) + (setf (cdr res) z) + z) + (cdr res))))) + + diff --git a/csys/dump-flic.scm b/csys/dump-flic.scm new file mode 100644 index 0000000..0fc654d --- /dev/null +++ b/csys/dump-flic.scm @@ -0,0 +1,130 @@ +;;; dump-flic.scm -- general dump functions for flic structures +;;; +;;; author : Sandra Loosemore +;;; date : 24 Feb 1993 +;;; +;;; +;;; This stuff is used to write inline expansions to the interface file. +;;; + + +(define-flic-walker dump-flic (object var-renamings)) + +(define (dump-flic-list objects var-renamings) + (let ((result '())) + (dolist (o objects) + (push (dump-flic o var-renamings) result)) + `(list ,@(nreverse result)))) + +(define (dump-flic-top object) + (dump-flic object '())) + + +(define (make-temp-bindings-for-dump oldvars var-renamings) + (let ((vars '()) + (bindings '())) + (dolist (v oldvars) + (let ((var (def-name v)) + (temp (gensym))) + (push temp vars) + (push `(,temp (create-temp-var ',var)) bindings) + (push (cons v temp) var-renamings))) + (setf bindings (nreverse bindings)) + (setf vars (nreverse vars)) + (values vars bindings var-renamings))) + +(define-dump-flic flic-lambda (object var-renamings) + (multiple-value-bind (vars bindings var-renamings) + (make-temp-bindings-for-dump (flic-lambda-vars object) var-renamings) + `(let ,bindings + (make-flic-lambda + (list ,@vars) + ,(dump-flic (flic-lambda-body object) var-renamings))) + )) + +(define-dump-flic flic-let (object var-renamings) + (multiple-value-bind (vars bindings var-renamings) + (make-temp-bindings-for-dump (flic-let-bindings object) var-renamings) + `(let ,bindings + ,@(map (lambda (temp v) + `(setf (var-value ,temp) + ,(dump-flic (var-value v) var-renamings))) + vars + (flic-let-bindings object)) + (make-flic-let + (list ,@vars) + ,(dump-flic (flic-let-body object) var-renamings) + ',(flic-let-recursive? object))) + )) + +(define-dump-flic flic-app (object var-renamings) + `(make-flic-app + ,(dump-flic (flic-app-fn object) var-renamings) + ,(dump-flic-list (flic-app-args object) var-renamings) + ',(flic-app-saturated? object))) + +(define-dump-flic flic-ref (object var-renamings) + (let* ((var (flic-ref-var object)) + (entry (assq var var-renamings))) + (if entry + `(make-flic-ref ,(cdr entry)) + `(make-flic-ref ,(dump-object var))))) + +(define-dump-flic flic-const (object var-renamings) + (declare (ignore var-renamings)) + `(make-flic-const ',(flic-const-value object))) + +(define-dump-flic flic-pack (object var-renamings) + (declare (ignore var-renamings)) + `(make-flic-pack ,(dump-object (flic-pack-con object)))) + +(define-dump-flic flic-case-block (object var-renamings) + `(make-flic-case-block + ',(flic-case-block-block-name object) + ,(dump-flic-list (flic-case-block-exps object) var-renamings))) + +(define-dump-flic flic-return-from (object var-renamings) + `(make-flic-return-from + ',(flic-return-from-block-name object) + ,(dump-flic (flic-return-from-exp object) var-renamings))) + +(define-dump-flic flic-and (object var-renamings) + `(make-flic-and + ,(dump-flic-list (flic-and-exps object) var-renamings))) + +(define-dump-flic flic-if (object var-renamings) + `(make-flic-if + ,(dump-flic (flic-if-test-exp object) var-renamings) + ,(dump-flic (flic-if-then-exp object) var-renamings) + ,(dump-flic (flic-if-else-exp object) var-renamings))) + +(define-dump-flic flic-sel (object var-renamings) + `(make-flic-sel + ,(dump-object (flic-sel-con object)) + ,(flic-sel-i object) + ,(dump-flic (flic-sel-exp object) var-renamings))) + +(define-dump-flic flic-is-constructor (object var-renamings) + `(make-flic-is-constructor + ,(dump-object (flic-is-constructor-con object)) + ,(dump-flic (flic-is-constructor-exp object) var-renamings))) + +(define-dump-flic flic-con-number (object var-renamings) + `(make-flic-con-number + ,(dump-object (flic-con-number-type object)) + ,(dump-flic (flic-con-number-exp object) var-renamings))) + +(define-dump-flic flic-void (object var-renamings) + (declare (ignore object var-renamings)) + `(make-flic-void)) + + + + + + + + + + + diff --git a/csys/dump-interface.scm b/csys/dump-interface.scm new file mode 100644 index 0000000..37b3bbd --- /dev/null +++ b/csys/dump-interface.scm @@ -0,0 +1,800 @@ +;;; dump-interface.scm -- interface file writer/loader +;;; +;;; author : John & Sandra +;;; date : 8 Jul 1992 +;;; +;;; This writes binary interface files. A binary interface file is just +;;; a lisp (mumble) source file which directly builds the ast structure +;;; created by a compilation. These files could be stored in either +;;; source or binary (compiled lisp) form. + +;;; An interface may reference entities defined in other interfaces. +;;; To ensure consistancy between when an interface is written and +;;; when it is read back in, a stamp is assigned to all interface files +;;; which serves as a unique id. The stamps of all imported units are +;;; saved and examined at load time. + + + +;;;================================================================== +;;; Interface to compilation system +;;;================================================================== + + +;;; For compiled code, don't actually write out all the source code. +;;; Use a magic macro to memoize the form to be compiled. + +(define *form-to-compile* '#f) +(define *magic-file-to-compile* "$HASKELL/bin/magic.scm") + + +;;; The output from compiling the prelude can completely overwhelm +;;; the Lisp compiler. If this variable is a number, it specifies +;;; a "reasonable" number of top-level forms which can be compiled +;;; and write-compiled-code-file will try to break up the input +;;; code automagically. + +(define *magic-chunk-size* '#f) + + +;;; This is called to write both the code file and the interface file. + +(define (write-compiled-code-file filename code code-quality chunk-size) + (let ((phase-start-time (get-run-time)) + (forms (flatten-forms code))) + (dynamic-let ((*magic-chunk-size* + (or chunk-size (dynamic *magic-chunk-size*))) + (*code-quality* + (or code-quality (dynamic *code-quality*)))) + (if (or (not (dynamic *magic-chunk-size*)) + (<= (the fixnum (length forms)) + (the fixnum (dynamic *magic-chunk-size*)))) + (write-compiled-code-file-aux filename `(begin ,@forms)) + (with-compilation-unit () + (write-compiled-code-file-aux + filename + `(begin + ,@(map (lambda (f) `(load ,f)) + (write-compiled-code-file-split filename forms))) + )))) + (when (memq 'phase-time *printers*) + (let* ((current-time (get-run-time)) + (elapsed-time (- current-time phase-start-time))) + (format '#t "Lisp compilation complete: ~A seconds~%" elapsed-time))) + )) + +(define (write-compiled-code-file-split filename forms) + (let ((place (filename-place filename)) + (name (filename-name filename)) + (type (filename-type filename)) + (result '())) + (do ((i 0 (1+ i))) + ((null? forms)) + (multiple-value-bind (head tail) + (split-list forms (dynamic *magic-chunk-size*)) + (let ((fname + (assemble-filename + place (format '#f "~a-part~a" name i) type))) + (push fname result) + (write-compiled-code-file-aux fname `(begin ,@head)) + (setf forms tail)))) + (nreverse result))) + +(define (flatten-forms code) + (if (and (pair? code) (eq? (car code) 'begin)) + (nreverse (flatten-forms-aux (cdr code) '())) + (list code))) + +(define (flatten-forms-aux forms result) + (dolist (f forms) + (if (and (pair? f) (eq? (car f) 'begin)) + (setf result (flatten-forms-aux (cdr f) result)) + (push f result))) + result) + + +(define (write-compiled-code-file-aux filename code) + (dynamic-let ((*form-to-compile* code)) + (compile-file (dynamic *magic-file-to-compile*) filename))) + +(define-syntax (magic-form-to-compile) + (dynamic *form-to-compile*)) + + +;;; Writing source code is good for debugging purposes, but slow. +;;; The *print-circle* and *print-shared* flags have to be set because +;;; the code printed out may contain gensyms, and this will ensure +;;; that the code can be read in again. + +(define (write-interpreted-code-file filename code hairy?) + (dynamic-let ((*print-circle* '#t) + (*print-shared* '#t)) + (call-with-output-file + filename + (lambda (port) + (if hairy? + (pprint-flatten code port) + (print-flatten code port)))))) + + +;;; This attempts to read a compiled interface for a unit. This is +;;; done whenever the unit file is newer than the source file. If +;;; imported units have changed, the load will fail and recompilation +;;; will be attempted. +;;; The caller is responsible for making sure that the interface file exists +;;; and for making sure that the interface file is up-to-date with +;;; respect to imported modules and that all the imported modules are +;;; known. + +;;; These variables are assigned by the code in the dump file. + +(define *modules-loaded* '()) +(define *modules-imported* '()) +(define *defs-referenced* '()) +(define *saved-cse-values* '()) +(define *writer-version* '()) + +(define (read-binary-interface unit) + (dynamic-let ((*modules-loaded* '()) + (*modules-imported* '()) + (*defs-referenced* '()) + (*saved-cse-values* '()) + (*writer-version* '())) + (let ((file-date + (load-more-recent-file (ucache-cifile unit) (ucache-sifile unit)))) + (cond ((string=? *writer-version* *haskell-compiler-version*) + (setf (ucache-idate unit) file-date) + (setf (ucache-modules unit) (vector->list *modules-loaded*)) + (setf (ucache-ifile-loaded unit) '#t) + '#t) + (else + (signal-incompatible-interface-file (ucache-cifile unit)) + '#f))))) + +(define (signal-incompatible-interface-file filename) + (fatal-error 'incompatible-interface-file + "File ~A~%~ + was written by a different version of the Haskell system.~%~ + You must remove it and recompile." + filename)) + + +(define (load-more-recent-file cfile sfile) + (cond ((file-exists? cfile) + (if (or (not (file-exists? sfile)) + (> (file-write-date cfile) + (file-write-date sfile))) + (load-compiled-interface-file cfile) + (load-interpreted-interface-file sfile))) + ((file-exists? sfile) + (load-interpreted-interface-file sfile)) + (else + (signal-file-not-found cfile)))) + +(define (load-interpreted-interface-file file) + (load file) + (file-write-date file)) + +(define (load-compiled-interface-file file) + (load file) + (file-write-date file)) + + +;;;================================================================== +;;; Dump code generator +;;;================================================================== + +;;; Globals + +(define *dump-defs* '()) +(define *dump-slot-init-code* '()) +(define *dump-def-counter* 0) +(define *dump-def-code-table* (make-table)) +(define *cse-objects* '()) +(define *cse-value-num* 0) +(define *cse-object-num* '()) +(define *gtype-class-index* '()) +(define *context-class-index* '()) +(define *gtype-tycon-index* '()) +(define *gtype-list-index* '()) +(define *gtype-index* '()) +(define *number-vars-dumped* 0) + + +(define-syntax (def-dump-code def) + `(table-entry *dump-def-code-table* ,def)) + +;;; This saves slot initialization code. + +(define (add-dump-init code) + (push code *dump-slot-init-code*)) + + +;;; Here is the top-level call. + +(define (create-dump-code unit modules load-prelude?) + (dynamic-let ((*unit* (module-unit (car modules))) + (*dump-defs* '()) + (*dump-slot-init-code* '()) + (*dump-def-counter* 0) + (*dump-def-code-table* (make-table)) + (*cse-objects* '()) + (*cse-object-num* *num-saved-gtyvars*) + (*gtype-class-index* '()) + (*context-class-index* '()) + (*gtype-tycon-index* '()) + (*gtype-list-index* '()) + (*gtype-index* '()) + (*number-vars-dumped* 0) + (*number-types-dumped* 0) + (*number-classes-dumped* 0)) + (let ((res (create-dump-code-aux unit modules load-prelude?))) + (when (memq 'dumper (dynamic *printers*)) + (pprint* res)) + (when (memq 'dump-stat (dynamic *printers*)) + (format '#t + "~&Dumped ~A definitions, ~A type objects, and ~A classes.~%" + *number-vars-dumped* *number-types-dumped* + *number-classes-dumped*) + (format '#t "Used ~A definitions and ~A type cells.~%" + *dump-def-counter* (length *cse-objects*))) + res))) + +;;; This assumes all modules are in the same compilation unit and that +;;; *unit* is set to that unit. +;;; imod-code establishes local bindings for all the imported modules. +;;; dmod-code establishes local bindings for all the modules defined in +;;; this compilation unit. + +(define (create-dump-code-aux unit modules load-prelude?) + (let* ((imod-counter 0) + (imod-alist '()) + (explicit-imports (collect-all-imported-modules unit)) + (all-imports (if load-prelude? + (append (collect-prelude-modules) explicit-imports) + explicit-imports)) + (imod-code (map (lambda (m) + (push (cons (module-name m) imod-counter) + imod-alist) + (incf imod-counter) + `(locate-module ',(module-name m))) + all-imports)) + (dmod-counter 0) + (dmod-alist '()) + (dmod-code (map (lambda (m) + (push (cons (module-name m) dmod-counter) + dmod-alist) + (incf dmod-counter) + `(make module + (unit ',(module-unit m)) + (name ',(module-name m)) + (type ',(module-type m)))) + modules))) + ;; This actually does most of the work. It dumps the module asts by + ;; placing inits for each slot into *dump-slot-init-code*. A list of + ;; definitions referenced is maintained in *dump-defs*. + (dolist (m modules) + (dump-module m (cdr (assq (module-name m) dmod-alist)))) + ;; This creates the final code + `(begin + (setf *writer-version* ',*haskell-compiler-version*) + (setf *modules-imported* (vector ,@imod-code)) + (setf *modules-loaded* (vector ,@dmod-code)) + ;; This sets the elements individually instead of using the vector + ;; function, because the vector may be longer than + ;; call-arguments-limit. + (setf *defs-referenced* + (make-vector ,(dynamic *dump-def-counter*))) + ,@(map (lambda (d) + `(setf ,(def-dump-code d) + ,(make-def-init-code d imod-alist dmod-alist))) + *dump-defs*) + ,@(cse-init-code) + ,@(dynamic *dump-slot-init-code*) + ) + )) + + +;;; Runtime support + +(define-syntax (lookup-imported-mod i) + `(vector-ref *modules-imported* ,i)) + +(define-syntax (lookup-defined-mod i) + `(vector-ref *modules-loaded* ,i)) + +(define (set-export-from-def-vector table key index) + (setf (table-entry table key) + (list (cons key (vector-ref *defs-referenced* index))))) + +(define (set-export-from-def table key def) + (setf (table-entry table key) + (list (cons key def)))) + +(define (set-symtab-from-def-vector table key index) + (setf (table-entry table key) + (vector-ref *defs-referenced* index))) + +(define (init-variable-slots var exported? toplevel? type simple? strict?) + (setf (def-exported? var) exported?) + (setf (var-toplevel? var) toplevel?) + (setf (var-type var) type) + (setf (var-simple? var) simple?) + (setf (var-strict? var) strict?) + var) + +(define (init-function-slots var exported? toplevel? type simple? strict? + arity strictness opt-entry) + (setf (def-exported? var) exported?) + (setf (var-toplevel? var) toplevel?) + (setf (var-type var) type) + (setf (var-simple? var) simple?) + (setf (var-strict? var) strict?) + (setf (var-arity var) arity) + (setf (var-strictness var) strictness) + (setf (var-optimized-entry var) opt-entry) + var) + +(define (init-method-var-slots var class default method-signature) + (setf (method-var-class var) class) + (setf (method-var-default var) default) + (setf (method-var-method-signature var) method-signature) + var) + +(define (init-constructor-slots + con arity types signature tag alg fixity infix?) + (setf (con-arity con) arity) + (setf (con-types con) types) + (setf (con-signature con) signature) + (setf (con-tag con) tag) + (setf (con-alg con) alg) + (setf (con-fixity con) fixity) + (setf (con-infix? con) infix?) + (dotimes (i arity) + (push '#f (con-slot-strict? con))) + con) + +(define (make-new-instance algdata tyvars class context gcontext dictionary m) + (make instance + (algdata algdata) + (tyvars tyvars) + (class class) + (context context) + (gcontext gcontext) + (dictionary dictionary) + (methods m) + (ok? '#t))) + + +;;; This computes the transitive closure of all modules available to +;;; a unit. + +(define (collect-all-imported-modules unit) + (collect-all-modules-1 (ucache-imported-units unit) '() '())) + +(define (collect-all-modules-1 units mods-so-far units-seen) + (cond ((null? units) + mods-so-far) + ((mem-string (car units) units-seen) + (collect-all-modules-1 (cdr units) mods-so-far units-seen)) + (else + (let ((u (lookup-compilation-unit (car units)))) + (collect-all-modules-1 + (append (ucache-imported-units u) (cdr units)) + (append (ucache-modules u) mods-so-far) + (cons (ucache-ufile u) units-seen)))) + )) + +(define (collect-prelude-modules) + (let ((prelude-unit (lookup-compilation-unit *prelude-unit-filename*))) + (append (ucache-modules prelude-unit) + (collect-all-imported-modules prelude-unit)))) + +(define (def->core-name-string def) + (if (con? def) + (remove-con-prefix (symbol->string (def-name def))) + (symbol->string (def-name def)))) + +;;; This code returns the load time definition for an object. When the +;;; object is a core symbol or in a different unit, previously +;;; created definitions are returned. Otherwise, a new definition is +;;; created. + +(define (make-def-init-code d imod-alist dmod-alist) + (declare (ignore dmod-alist)) + (cond ((def-core? d) + `(core-symbol ,(def->core-name-string d))) + ((eq? (def-unit d) *unit*) + `(create-definition/inner + ',(def-module d) + ',(def-name d) + ',(cond ((method-var? d) 'method-var) + ((var? d) 'var) + ((con? d) 'con) + ((synonym? d) 'synonym) + ((algdata? d) 'algdata) + ((class? d) 'class)))) + ((is-tuple-constructor? d) + `(tuple-constructor ,(tuple-constructor-arity d))) + ((is-tuple-tycon? d) + `(tuple-tycon ,(tuple-constructor-arity (car (algdata-constrs d))))) + (else + (let ((m (assq (def-module d) imod-alist))) + ;; This is a bogus error message. The problem is that nothing + ;; so far ensures units are closed under import/export: some + ;; modules may be referenced that are accidentally in the symbol + ;; table. The unif file for the current module needs to be + ;; updated when this happens. + (when (eq? m '#f) + (fatal-error 'symbol-not-in-unit + "Reference to symbol ~A in module ~A: not in compilation unit.~%" + (def-name d) (def-module d))) + `(table-entry + (module-symbol-table + (lookup-imported-mod ,(tuple-2-2 m))) + ',(def-name d)))) + )) + + +;;; Once a module has been compiled, most of its slots are useless. +;;; All we really need to save are the identifying information, +;;; symbol table, and export table. +;;; Instances also need to be dumped here instead of with class objects; +;;; this is because links can go across compilation unit boundaries. +;;; They are fixed up when pulling units out of the cache. +;;; The identifying info is stored when the module variable is bound. + + +(define (dump-module module index) + (let ((mod-exp `(lookup-defined-mod ,index)) + (save-all-symbols (or (eq? (module-type module) 'standard) + (eq? (module-name module) '|Prelude|)))) + ;; Dump symbol table entries only for defs for which this is + ;; the "home" module. (In other words, ignore imported defs.) + ;; The purpose of this is to allow references from other + ;; interface files to be resolved; see make-def-init-code. + ;; Jcp: we need to save the complete symbol table for incremental + ;; compilation to work. + (let ((code '())) + (table-for-each + (lambda (key val) + (when (or save-all-symbols + (eq? (def-module val) (module-name module))) + (let ((def (dump-object val))) + (push + (if (and (pair? def) + (eq? (car def) 'vector-ref) + (eq? (cadr def) '*defs-referenced*)) + `(set-symtab-from-def-vector table ',key ,(caddr def)) + `(setf (table-entry table ',key) ,def)) + code)))) + (module-symbol-table module)) + (add-dump-init `(setf (module-symbol-table ,mod-exp) + (let ((table (make-table))) ,@code table)))) + ;; dump the fixity table - needed by the incremental compiler + (when save-all-symbols + (let ((code '())) + (table-for-each + (lambda (key val) + (push `(setf (table-entry table ',key) + (make-fixity ',(fixity-associativity val) + ',(fixity-precedence val))) + code)) + (module-fixity-table module)) + (add-dump-init `(setf (module-fixity-table ,mod-exp) + (let ((table (make-table))) ,@code table))))) + ;; Dump all export table entries. This is used by the import/export + ;; phase to resolve references. + (let ((code '())) + (table-for-each + (lambda (key val) + ;; val is an a-list of (sym . def) pairs. + ;; Look for shortcut to reduce size of generated code. + (push + (if (and (null? (cdr val)) + (eq? (car (car val)) key)) + (let ((def (dump-object (cdr (car val))))) + (if (and (pair? def) + (eq? (car def) 'vector-ref) + (eq? (cadr def) '*defs-referenced*)) + `(set-export-from-def-vector table ',key ,(caddr def)) + `(set-export-from-def table ',key ,def))) + `(setf (table-entry table ',key) ,(dump-object val))) + code)) + (module-export-table module)) + (add-dump-init `(setf (module-export-table ,mod-exp) + (let ((table (make-table))) ,@code table)))) + ;; Dump the instances. + (add-dump-init `(setf (module-instance-defs ,mod-exp) + ,(dump-object (module-instance-defs module)))) + (add-dump-init `(setf (module-default ,mod-exp) + ,(dump-object (module-default module)))) + (add-dump-init `(setf (module-uses-standard-prelude? ,mod-exp) + ,(dump-object + (module-uses-standard-prelude? module)))) + )) + +(define (make-fixity a p) + (make fixity (associativity a) (precedence p))) + + +;;;================================================================== +;;; Dump structure traversal +;;;================================================================== + +;;; This is the general object dumper. It recognizes the basic Lisp +;;; objects and dumps them. Given an object, this generates lisp code +;;; to recreate the object at load time. + +(define (dump-object x) + (cond ((struct? x) + (dump x)) + ((or (symbol? x) (null? x)) + ;; Symbols and lists must be quoted. + `',x) + ((or (number? x) + (eq? x '#t) + (eq? x '#f) + (string? x) ; This makes dumped strings immutable. + (char? x)) + ;; These objects are self-evaluating. + x) + ((list? x) + ;; True lists + `(list ,@(map (function dump-object) x))) + ((pair? x) + `(cons ,(dump-object (car x)) + ,(dump-object (cdr x)))) + ((vector? x) + `(vector ,@(map (function dump-object) (vector->list x)))) + ((table? x) + `(list->table ,@(dump-object (table->list x)))) + (else + (error "Don't know how to dump ~A." x)))) + + +;;; *** Should install the walker in the type descriptor. + +(define-walker dump) + +(define (dump x) + (call-walker dump x)) + + + +;;;================================================================== +;;; Dumpers for defs +;;;================================================================== + + +;;; All walkers for def structures should call this macro. The body +;;; is invoked only if the def belongs to the current compilation unit +;;; and hasn't already been traversed. Within the body, the +;;; variable "v" is bound to a form that will evaluate to the +;;; corresponding def structure at run time. This is also +;;; the return value from the macro. + +(define-local-syntax (with-new-def (v d stat-var) . body) + (let ((temp (gensym)) + (expvar (gensym))) + `(let ((,temp ,d) + (,expvar '#f)) + (if (not (def-dump-code ,temp)) + (begin + (cond ((not (def-core? ,temp)) + (setf ,expvar + (list 'vector-ref + '*defs-referenced* + (dynamic *dump-def-counter*))) + (incf (dynamic *dump-def-counter*)) + (push ,temp *dump-defs*)) + (else + (setf ,expvar + (make-core-symbol-name + (def->core-name-string ,temp))))) + (setf (def-dump-code ,temp) ,expvar) + (when (eq? (def-unit ,temp) *unit*) + (incf (dynamic ,stat-var)) + (let ((,v ,expvar)) + ,@body)) + ,expvar) + (def-dump-code ,temp))))) + + +;;; This macro is used to save the value of a structure slot in the +;;; initforms of the dump. + +(define-local-syntax (dump-def-slots obj-var type dexp slots) + `(add-dump-init + (list 'update-slots ',type ,dexp + ,@(map (lambda (s) + `(list ',s + (dump-object (struct-slot ',type ',s ,obj-var)))) + slots))) + ) + + + +(define-walker-method dump var (var) + (dump-var/n var)) + +(define (dump-var/n var) + (with-new-def (dexp var *number-vars-dumped*) + (do-dump-var dexp var '#f))) + +(define (do-dump-var dexp var method-var?) + (let ((code '()) + (exported? (def-exported? var)) + (toplevel? (var-toplevel? var)) + (type (var-type var)) + (simple? (var-simple? var)) + (strict? (var-strict? var)) + (arity (var-arity var)) + (strictness (var-strictness var)) + (opt-entry (var-optimized-entry var)) + (complexity (var-complexity var)) + (fixity (var-fixity var)) + (value (var-value var)) + (inline-value (var-inline-value var)) + (sel? (var-selector-fn? var))) + ;; Some slots are useless for vars that don't name functions. + (if (eqv? arity 0) + (push `(init-variable-slots var + ',exported? + ',toplevel? + ,(dump-object type) + ',simple? + ',strict?) + code) + (push `(init-function-slots var + ',exported? + ',toplevel? + ,(dump-object type) + ',simple? + ',strict? + ',arity + ,(dump-strictness strictness) + ',opt-entry) + code)) + ;; These slots rarely need to be tweaked from the default. + (when sel? + (push `(setf (var-selector-fn? var) '#t) code)) + (when complexity + (push `(setf (var-complexity var) ,complexity) code)) + (when fixity + (push `(setf (var-fixity var) ,(dump-object fixity)) code)) + ;; Save values of simple variables to permit inlining. + ;; Save values of structured constants to permit folding of flic-sel + ;; operations -- this is necessary to optimize dictionary lookups. + (when (or simple? sel? + (and value + (is-type? 'flic-app value) + (structured-constant-app? + (flic-app-fn value) (flic-app-args value)))) + (push `(setf (var-value var) ,(dump-flic-top value)) code)) + (when inline-value + (push `(setf (var-inline-value var) ,(dump-flic-top inline-value)) code)) + ;; Save extra stuff for method vars + (when method-var? + (push `(init-method-var-slots var + ,(dump-object (method-var-class var)) + ,(dump-object (method-var-default var)) + ,(dump-object (method-var-method-signature var))) + code)) + ;; Push the whole mess onto the init code. + (add-dump-init `(let ((var ,dexp)) ,@(nreverse code))))) + + +(define-walker-method dump method-var (var) + (dump-method-var/n var)) + +(define (dump-method-var/n var) + (with-new-def (dexp var *number-vars-dumped*) + (do-dump-var dexp var '#t))) + +(define-walker-method dump con (con) + (dump-con/n con)) + +(define (dump-con/n con) + (with-new-def (dexp con *number-types-dumped*) + (add-dump-init + `(let ((con (init-constructor-slots + ,dexp + ,(con-arity con) + ,(dump-object (con-types con)) + ,(dump-object (con-signature con)) + ,(con-tag con) + ,(dump-object (con-alg con)) + ,(dump-object (con-fixity con)) + ',(con-infix? con)))) + ,@(if (memq '#t (con-slot-strict? con)) + `((setf (con-slot-strict? con) ',(con-slot-strict? con))) + '()) + ,@(if (eq? (con-lisp-fns con) '()) + '() + `((setf (con-lisp-fns con) ',(con-lisp-fns con)))) + con)))) + +;;; *** Could define similar init functions for other defs instead +;;; *** of setting slots inline, but I'm lazy and they don't show up +;;; *** nearly as often as the others. + +(define-walker-method dump algdata (alg) + (dump-algdata/n alg)) + +(define (dump-algdata/n alg) + (with-new-def (dexp alg *number-types-dumped*) + (dump-def-slots alg algdata dexp + (arity n-constr constrs context tyvars signature + enum? tuple? real-tuple? implemented-by-lisp?)))) + +(define-walker-method dump synonym (syn) + (dump-synonym/n syn)) + +(define (dump-synonym/n syn) + (with-new-def (dexp syn *number-types-dumped*) + (dump-def-slots syn synonym dexp (arity args body)))) + +(define-walker-method dump class (class) + (dump-class/n class)) + +(define (dump-class/n class) + (with-new-def (dexp class *number-classes-dumped*) + (dump-def-slots class class dexp + (super super* tyvar method-vars selectors kind + n-methods dict-size)))) + + +;;;================================================================== +;;; Dumpers for non-def AST structs +;;;================================================================== + +;;; This section contains dumpers to handle type-related structs that +;;; are referenced by the various def guys. + + +(define-walker-method dump instance (o) + (if (not (instance-ok? o)) + (error "Attempt to dump instance that's not ok!")) + `(make-new-instance + ,(dump-object (instance-algdata o)) + ,(dump-object (instance-tyvars o)) + ,(dump-object (instance-class o)) + ,(dump-object (instance-context o)) + ,(dump-object (instance-gcontext o)) + ,(dump-object (instance-dictionary o)) + ,(dump-object (instance-methods o)))) + + + +(define-walker-method dump gtype (o) + (dump-gtype/cse o)) + +(define-walker-method dump fixity (o) + `(**fixity ',(fixity-associativity o) ',(fixity-precedence o))) + +(define-walker-method dump tyvar (o) + `(**tyvar ',(tyvar-name o))) + +(define-walker-method dump class-ref (o) + `(**class/def ,(dump-object (class-ref-class o)))) + +(define-walker-method dump context (o) + `(**context ,(dump-object (context-class o)) + ,(dump-object (context-tyvar o)))) + +(define-walker-method dump tycon (o) + `(**tycon/def ,(dump-object (tycon-def o)) + ,(dump-object (tycon-args o)))) + +(define-walker-method dump default-decl (o) + `(make default-decl (types ,(dump-object (default-decl-types o))))) + +(define-walker-method dump signature (o) + `(make signature (context ,(dump-object (signature-context o))) + (type ,(dump-object (signature-type o))))) + +;;; All ntyvars should be instantiated at this point + +; (define-walker-method dump ntyvar (o) +; (dump-object (prune o))) diff --git a/csys/dump-macros.scm b/csys/dump-macros.scm new file mode 100644 index 0000000..404adf8 --- /dev/null +++ b/csys/dump-macros.scm @@ -0,0 +1,37 @@ +(define-syntax (set-strictness-vars) + (let ((res '())) + (dotimes (i *pre-defined-strictness-vars*) + (push `(setf (dynamic ,(vector-ref *pre-defined-strictness-names* i)) + (vector-ref *pre-defined-strictness-table* ',i)) + res)) + `(begin ,@res))) + +(define-syntax (setup-gtyvar-vars) + (let ((res '())) + (dotimes (i *num-saved-gtyvars*) + (push `(setf (dynamic ,(vector-ref *saved-gtyvar-varnames* i)) + (vector-ref *saved-gtyvars* ',i)) + res)) + `(begin ,@res))) + +(define-syntax (assq/insert x table) + `(let ((res (assq ,x ,table))) + (if (eqv? res '#f) + (begin + (let ((new-pair (cons ,x '#f))) + (push new-pair ,table) + new-pair)) + res))) + +(define-syntax (assq/insert-l x table) + `(let ((res (assq ,x ,table))) + (if (eqv? res '#f) + (begin + (let ((new-pair (cons ,x '()))) + (push new-pair ,table) + new-pair)) + res))) + + + + diff --git a/csys/dump-params.scm b/csys/dump-params.scm new file mode 100644 index 0000000..cabbfd0 --- /dev/null +++ b/csys/dump-params.scm @@ -0,0 +1,18 @@ +(define *num-saved-gtyvars* 19) +(define *pre-defined-strictness-size* 7) ; length of max strictness list +(define *pre-defined-strictness-table* '()) +(define *pre-defined-strictness-vars* 32) ; number of global vars +(define *pre-defined-strictness-names* + (make-vector *pre-defined-strictness-vars*)) + +(dotimes (i *pre-defined-strictness-vars*) + (setf (vector-ref *pre-defined-strictness-names* i) + (string->symbol (format '#f "SAVED-STRICTNESS-~A" i)))) + +(define *saved-gtyvars* '()) +(define *saved-gtyvar-varnames* (make-vector *num-saved-gtyvars*)) +(dotimes (i *num-saved-gtyvars*) + (setf (vector-ref *saved-gtyvar-varnames* i) + (string->symbol (format '#f "SAVED-GTYVAR-NAME~A" i)))) + + diff --git a/csys/magic.scm b/csys/magic.scm new file mode 100644 index 0000000..999e8b0 --- /dev/null +++ b/csys/magic.scm @@ -0,0 +1,10 @@ +;;; magic.scm -- magic support file for dumping compiled code files. +;;; +;;; author : Sandra Loosemore +;;; date : 8 Jul 1992 +;;; +;;; This file is used to dump compiled code files. The macro call below +;;; expands into the code being dumped. See dump-interface.scm for more +;;; details. + +(magic-form-to-compile) |