; Copyright (c) 1994 by Richard Kelsey. See file COPYING. ; Definitions are ( . ) pairs, where can be any ; Scheme value. This code walks the values looking for sharing and for ; closures. Shared values are collected in a list and additional definitions ; are introduced for the bindings in the environments of closures and for ; close-compiled versions of any primitives in non-call position. References ; to closure-bound variables are replaced with references to the newly-created ; package variables. (define (flatten-definitions definitions) (set! *shared* '()) (set! *definitions* '()) (set! *immutable-value-table* (make-value-table)) (set! *closed-compiled-primitives* (make-symbol-table)) (let loop ((defs definitions) (flat '())) (cond ((not (null? defs)) (let ((var (caar defs)) (value (cdar defs))) (if (and (variable-set!? var) (closure? value)) (let ((new (generate-top-variable (variable-name var)))) (loop `((,var . ,new) (,new . ,value) . ,defs) flat)) (loop (cdr defs) (cons (cons var (flatten-value value)) flat))))) ((null? *definitions*) (let ((forms (really-make-forms flat *shared*))) (set! *shared* #f) ; safety (set! *closed-compiled-primitives* #f) (set! *immutable-value-table* #f) forms)) (else (let ((defs *definitions*)) (set! *definitions* '()) (loop defs flat)))))) ; is a list of ( . ) pairs. ; is a list of all shared objects, each of which must end up being ; bound to a variable. (define (really-make-forms definitions shared) (for-each (lambda (defn) (let ((var (car defn)) (shared (value-shared (cdr defn)))) (if (and (not (variable-set!? var)) shared (not (shared-variable shared))) (set-shared-variable! shared var)))) definitions) (map definition->form (append definitions (shared-values->definitions shared)))) (define variable-set!? (structure-ref forms variable-set!?)) (define (shared-values->definitions shared) (do ((shared shared (cdr shared)) (defns '() (if (shared-variable (value-shared (car shared))) defns (let ((var (generate-top-variable #f))) (set-shared-variable! (value-shared (car shared)) var) (cons (cons var (car shared)) defns))))) ((null? shared) defns))) (define (definition->form definition) (let* ((var (car definition)) (value (cdr definition)) (shared (value-shared value)) (value (if (or (not shared) (eq? var (shared-variable shared))) value (shared-variable shared))) (clean (clean-value! value))) ((structure-ref forms make-form) var (if (or (node? clean) (variable? clean)) clean (make-literal-node clean)) (if (closure? value) (cdr (shared-saved (closure-temp value))) ; free vars (stored-value-free-vars clean))))) (define (make-literal-node value) (make-node op/literal value)) (define (make-name-node value) (make-node op/name value)) (define *shared* '()) (define (add-shared! value) (set! *shared* (cons value *shared*))) (define *definitions* '()) (define (add-package-definition! value id) (let ((var (generate-top-variable id))) (set! *definitions* (cons (cons var value) *definitions*)) var)) (define (generate-top-variable maybe-id) (let ((var (make-global-variable (concatenate-symbol (if maybe-id (schemify maybe-id) 'top.) (next-top-id)) type/undetermined))) (set-variable-flags! var (cons 'generated-top-variable (variable-flags var))) var)) (define *next-top-id* 0) (define (next-top-id) (let ((id *next-top-id*)) (set! *next-top-id* (+ 1 *next-top-id*)) id)) (define (generated-top-variable? var) (memq? 'generated-top-variable (variable-flags var))) (define (stored-value-free-vars value) (let ((vars '())) (let label ((value value)) (cond ((variable? value) (cond ((not (variable-flag value)) (set-variable-flag! value #t) (set! vars (cons value vars))) (else ;(breakpoint "marked variable") ; why did I care? (values)))) ((vector? value) (do ((i 0 (+ i 1))) ((= i (vector-length value))) (label (vector-ref value i)))) ((pair? value) (label (car value)) (label (cdr value))))) (for-each (lambda (var) (set-variable-flag! var #f)) vars) vars)) ;---------------------------------------------------------------- ; Finding shared data structures. (define-record-type shared () (saved (shared? #f) (variable #f))) (define make-shared shared-maker) (define (value-shared value) (cond ((pair? value) (car value)) ((vector? value) (if (= 0 (vector-length value)) #f (vector-ref value 0))) ((closure? value) (closure-temp value)) (else #f))) (define (clean-value! value) (cond ((pair? value) (cons (clean-sub-value! (shared-saved (car value))) (clean-sub-value! (cdr value)))) ((vector? value) (if (= 0 (vector-length value)) value (let ((new (make-vector (vector-length value)))) (vector-set! new 0 (clean-sub-value! (shared-saved (vector-ref value 0)))) (do ((i 1 (+ i 1))) ((= i (vector-length value))) (vector-set! new i (clean-sub-value! (vector-ref value i)))) new))) ((closure? value) (car (shared-saved (closure-temp value)))) ; flattened version of node ((node? value) (if (name-node? value) (name-node->variable value) (bug "bad definition value: ~S" value))) (else value))) (define name-node? (node-predicate 'name)) (define (clean-sub-value! value) (cond ((pair? value) (let ((shared (car value))) (cond ((shared-shared? shared) (shared-variable shared)) (else (set-car! value (clean-sub-value! (shared-saved shared))) (set-cdr! value (clean-sub-value! (cdr value))) value)))) ((vector? value) (cond ((= 0 (vector-length value)) value) ((shared-shared? (vector-ref value 0)) (shared-variable (vector-ref value 0))) (else (vector-set! value 0 (clean-sub-value! (shared-saved (vector-ref value 0)))) (do ((i 1 (+ i 1))) ((= i (vector-length value))) (vector-set! value i (clean-sub-value! (vector-ref value i)))) value))) ((closure? value) (shared-variable (closure-temp value))) (else value))) (define (flatten-value value) (cond ((immutable? value) (flatten-immutable-value value)) ((primitive? value) (primitive->name-node value)) (else (flatten-value! value) value))) (define (flatten-value! value) (cond ((pair? value) (check-shared! (car value) flatten-pair! value)) ((vector? value) (if (not (= 0 (vector-length value))) (check-shared! (vector-ref value 0) flatten-vector! value))) ((closure? value) (check-shared! (closure-temp value) flatten-closure! value)))) (define (check-shared! shared flatten! value) (cond ((not (shared? shared)) (flatten! value)) ((not (shared-shared? shared)) (set-shared-shared?! shared #t) (add-shared! value)))) (define *immutable-value-table* #f) (define (flatten-immutable-value value) (cond ((pair? value) (or (shared-immutable-value value car) (let ((p (cons (car value) (cdr value)))) (table-set! *immutable-value-table* value p) (flatten-pair! p) p))) ((vector? value) (if (= 0 (vector-length value)) value (or (shared-immutable-value value (lambda (x) (vector-ref x 0))) (let ((v (copy-vector value))) (table-set! *immutable-value-table* value v) (flatten-vector! v) v)))) ; no immutable closures (else value))) ; no sub-values (define (shared-immutable-value value accessor) (cond ((table-ref *immutable-value-table* value) => (lambda (copy) (cond ((not (shared-shared? (accessor copy))) (set-shared-shared?! (accessor copy) #t) (add-shared! copy) copy)))) (else #f))) (define (flatten-pair! pair) (let ((temp (car pair)) (shared (make-shared))) (set-car! pair shared) (set-shared-saved! shared (flatten-value temp)) (set-cdr! pair (flatten-value (cdr pair))))) (define (flatten-vector! vector) (let ((temp (vector-ref vector 0)) (shared (make-shared))) (vector-set! vector 0 shared) (set-shared-saved! shared (flatten-value temp)) (do ((i 1 (+ i 1))) ((= i (vector-length vector))) (vector-set! vector i (flatten-value (vector-ref vector i)))))) ; Make top-level definitions for the bindings in the closure and then substitute ; the defined variables within the closure's code. The define variables are ; saved in the bindings in case they are shared with other closures (both for ; efficiency and because SET! requires it). (define (flatten-closure! closure) (let ((shared (make-shared))) (for-each flatten-closure-binding! (closure-env closure)) (set-closure-temp! closure shared) (set-shared-shared?! shared #t) ; closures always need definitions (add-shared! closure) (receive (exp free) (substitute-in-expression (closure-node closure)) (set-shared-saved! shared (cons exp free)) (for-each clear-closure-binding! (closure-env closure))))) (define (clear-closure-binding! pair) (node-set! (car pair) 'substitute #f)) ; PAIR is ( . ) if it hasn't been seen before and ; ( . ) if it has. (define (flatten-closure-binding! pair) (let* ((name (car pair)) (subst (if (node? (cdr pair)) (cdr pair) (let ((subst (make-name-node-subst name (cdr pair)))) (set-cdr! pair subst) subst)))) (node-set! name 'substitute subst))) (define (make-name-node-subst name value) (let ((var (add-package-definition! value (node-form name))) (subst (make-similar-node name (node-form name)))) (node-set! subst 'binding (make-binding #f var #f)) subst)) (define op/literal (get-operator 'literal)) (define op/name (get-operator 'name)) ;---------------------------------------------------------------- (define *closed-compiled-primitives* #f) (define (make-primitive-node primitive call?) (if (and call? (primitive-expands-in-place? primitive)) (make-node op/primitive primitive) (let ((name-node (primitive->name-node primitive))) (note-variable-use! (name-node->variable name-node)) name-node))) (define (name-node->variable name-node) (let ((binding (node-ref name-node 'binding))) (cond ((not (binding? binding)) (bug "unbound variable ~S" (node-form name-node))) ((primitive? (binding-static binding)) (primitive->name-node (binding-static binding))) (else (binding-place binding))))) (define (primitive->name-node primitive) (let ((id (primitive-id primitive))) (or (table-ref *closed-compiled-primitives* id) (let* ((var (add-package-definition! (make-top-level-closure (expand (primitive-source primitive) prescheme-compiler-env)) id)) (binding (make-binding #f var #f)) (node (make-node op/name id))) (node-set! node 'binding (make-binding #f var #f)) (table-set! *closed-compiled-primitives* id node) (set-variable-flags! var (cons 'closed-compiled-primitive (variable-flags var))) node)))) (define op/primitive (get-operator 'primitive)) ;---------------------------------------------------------------- (define max-key-depth 5) (define (value-table-hash-function obj) (let recur ((obj obj) (depth 0)) (cond ((= depth max-key-depth) 0) ((symbol? obj) (string-hash (symbol->string obj))) ((integer? obj) (if (< obj 0) (- -1 obj) obj)) ((char? obj) (+ 333 (char->integer obj))) ((eq? obj #f) 3001) ((eq? obj #t) 3003) ((null? obj) 3005) ((pair? obj) (+ 3007 (recur (car obj) (- depth 1)) (* 3 (recur (cdr obj) (- depth 1))))) ((vector? obj) (let loop ((i 0) (hash (+ 3009 (vector-length obj)))) (if (or (= i (vector-length obj)) (= 0 (- depth i))) hash (loop (+ i 1) (+ hash (* i (recur (vector-ref obj i) (- depth i)))))))) (else (error "value cannot be used as a table key" obj))))) (define make-value-table (make-table-maker eq? value-table-hash-function))