; Copyright (c) 1994 by Richard Kelsey. See file COPYING. ; Substituting new variables for old in expressions. (define *free-exp-vars* #f) (define (substitute-in-expression exp) (set! *free-exp-vars* '()) (set! *letrec-datas* '()) (let* ((exp (substitute-in-exp exp)) (free *free-exp-vars*)) (set! *free-exp-vars* '()) (for-each (lambda (var) (set-variable-flag! var #f)) free) (values exp free))) (define global-marker (list 'global)) (define (note-binding-use! binding) (let ((var (binding-place binding))) (if (variable? var) (note-variable-use! var)))) (define (note-variable-use! var) (cond ((not (eq? (variable-flag var) global-marker)) (set! *free-exp-vars* (cons var *free-exp-vars*)) (set-variable-flag! var global-marker)))) ; Main dispatch (define (substitute-in-exp node) ((operator-table-ref substitutions (node-operator-id node)) node)) ; Particular operators (define substitutions (make-operator-table (lambda (node) (error "no substitution for node ~S" node)))) (define (default-substitution node) (make-similar-node node (cons (car (node-form node)) (map substitute-in-exp (cdr (node-form node)))))) (define (define-substitution name proc) (operator-define! substitutions name #f proc)) (define-substitution 'literal identity) (define-substitution 'quote identity) (define-substitution 'unspecific identity) (define-substitution 'real-external (lambda (node) (let* ((exp (node-form node)) (type (expand-type-spec (cadr (node-form (caddr exp)))))) (make-literal-node (make-external-value (node-form (cadr exp)) type))))) (define op/literal (get-operator 'literal)) (define (make-literal-node x) (make-node op/literal x)) ; We copy the names because the same node may occur in multiple places ; in the tree. (define-substitution 'lambda (lambda (node) (let* ((new-names (copy-names (cadr (node-form node)))) (body (substitute-in-exp (caddr (node-form node))))) (make-similar-node node (list (car (node-form node)) new-names body))))) (define (copy-names names) (map (lambda (name) (let ((new (make-similar-node name (node-form name)))) (node-set! name 'substitute new) new)) names)) (define-substitution 'name (lambda (node) (substitute-name-node node #f))) (define (substitute-name-node node call?) (let ((node (name-node-substitute node))) (let ((binding (node-ref node 'binding))) (cond ((not binding) (note-name-use! node) node) ((primitive? (binding-static binding)) (make-primitive-node (binding-static binding) call?)) ((location? (binding-place binding)) (let ((value (contents (binding-place binding)))) (if (constant? value) (make-literal-node value) (identity (bug "name ~S has non-constant location ~S" node value))))) (else (note-binding-use! binding) node))))) (define (name-node-substitute node) (let loop ((node node) (first? #t)) (cond ((node-ref node 'substitute) => (lambda (node) (loop node #f))) ((and first? (not (node-ref node 'binding))) (user-error "unbound variable ~S" (node-form node))) (else node)))) (define-substitution 'set! (lambda (node) (let* ((exp (node-form node)) (name (substitute-name-node (cadr exp) #f)) (binding (node-ref name 'binding))) (if (not (binding? binding)) (user-error "SET! on local variable ~S" (node-form (cadr exp)))) ((structure-ref forms note-variable-set!!) (binding-place binding)) (note-binding-use! binding) (make-similar-node node (list (car exp) name (substitute-in-exp (caddr exp))))))) (define-substitution 'call (lambda (node) (let ((proc (car (node-form node))) (args (cdr (node-form node)))) (make-similar-node node (cons (if (name-node? proc) (substitute-name-node proc #t) (substitute-in-exp proc)) (map substitute-in-exp args)))))) ; Flush GOTO when it is used with a primitive. (define-substitution 'goto (lambda (node) (let ((proc (cadr (node-form node))) (args (cddr (node-form node)))) (if (and (name-node? proc) (bound-to-primitive? proc)) (make-node (get-operator 'call) (cons (substitute-name-node proc #t) (map substitute-in-exp args))) (make-similar-node node (cons 'goto (cons (if (name-node? proc) (substitute-name-node proc #t) (substitute-in-exp proc)) (map substitute-in-exp args)))))))) (define name-node? (node-predicate 'name)) (define (bound-to-primitive? node) (let ((node (name-node-substitute node))) (let ((binding (node-ref node 'binding))) (and binding (primitive? (binding-static binding)))))) (define-substitution 'begin default-substitution) (define-substitution 'if default-substitution) ; drop the loophole part (define-substitution 'loophole (lambda (node) (substitute-in-exp (caddr (node-form node))))) ;---------------------------------------------------------------- ; Breaking LETREC's down to improve type inference and make compilation ; easier. (define-substitution 'letrec (lambda (node) (let* ((exp (node-form node)) (vars (map car (cadr exp))) (vals (map cadr (cadr exp)))) (receive (names datas) (copy-letrec-names vars vals exp) (for-each (lambda (data value) (expand-letrec-value data value datas exp)) datas vals) (let ((sets (strongly-connected-components datas letrec-data-uses letrec-data-seen? set-letrec-data-seen?!))) ;; so we don't keep track of which vars are referenced in the body (for-each (lambda (d) (set-letrec-data-seen?! d #t)) datas) (do ((sets sets (cdr sets)) (exp (substitute-in-exp (caddr exp)) (build-letrec (car sets) exp))) ((null? sets) (for-each (lambda (n) (node-set! n 'letrec-data #f)) names) exp))))))) (define-record-type letrec-data (name ; the name node for which this data exists marker ; a unique marker for this LETREC cell? ; variable is SET! or its value is not a (lambda ...). This is ; always #F until I can think of a reason to allow otherwise. ) (value ; the expanded value of this variable uses ; a list of variables that VALUE uses seen? ; #T if this has been seen before during the current expansion )) (define (copy-letrec-names names vals marker) (let ((names (map (lambda (name value) (let ((new (make-similar-node name (node-form name))) (cell? #f)) ; we no longer allow SET! on LETREC vars. (node-set! new 'letrec-data (letrec-data-maker new marker cell?)) (node-set! name 'substitute new) new)) names vals))) (values names (map (lambda (name) (node-ref name 'letrec-data)) names)))) (define lambda-node? (node-predicate 'lambda)) ; List of LETREC bound variables currently in scope. (define *letrec-datas* '()) (define (note-name-use! name) (let ((data (node-ref name 'letrec-data))) (cond ((and data (not (letrec-data-seen? data))) (set-letrec-data-seen?! data #t) (set! *letrec-datas* (cons data *letrec-datas*)))))) ; Expand VALUE and determine which of DATAS it uses. (define (expand-letrec-value data value datas mark) (let ((old-letrec-vars *letrec-datas*)) (set! *letrec-datas* '()) (for-each (lambda (d) (set-letrec-data-seen?! d #f)) datas) (set-letrec-data-value! data (substitute-in-exp value)) (receive (ours others) (partition-list (lambda (data) (eq? (letrec-data-marker data) mark)) *letrec-datas*) (set! *letrec-datas* (append others old-letrec-vars)) (set-letrec-data-uses! data ours)))) ; If there is only one variable and its value doesn't reference it, then ; use a LET instead of a LETREC. Variables whose value is either set! or ; not a lambda have explicit cells introduced. (define (build-letrec datas body) (if (and (null? (cdr datas)) (not (memq? (car datas) (letrec-data-uses (car datas))))) (make-let-node (map letrec-data-name datas) (map letrec-data-value datas) body) (receive (cells normal) (partition-list letrec-data-cell? datas) (make-let-node (map letrec-data-name cells) (map (lambda (ignore) (unspecific-node)) cells) (make-letrec-node (map letrec-data-name normal) (map letrec-data-value normal) (make-begin-node (append (map letrec-data->set! cells) (list body)))))))) (define op/unspecific (get-operator 'unspecific)) (define op/set! (get-operator 'set!)) (define (unspecific-node) (make-node op/unspecific '())) (define (letrec-data->set! data) (make-node op/set! (list 'set! (letrec-data-name data) (letrec-data-value data)))) (define (make-let-node names values body) (if (null? names) body (make-node op/call (cons (make-node op/lambda (list 'lambda names body)) values)))) (define (make-letrec-node names values body) (if (null? names) body (make-node op/letrec (list 'letrec (map list names values) body)))) (define (make-begin-node nodes) (if (null? (cdr nodes)) (car nodes) (make-node op/begin (cons 'begin nodes)))) (define op/call (get-operator 'call)) (define op/lambda (get-operator 'lambda)) (define op/letrec (get-operator 'letrec)) (define op/begin (get-operator 'begin)) ;---------------------------------------------------------------- ; A version of MAKE-SIMILAR-NODE that actually makes a new node. ; I wish this could keep the old node's list of properties. (define (make-similar-node node form) (make-node (node-operator node) form))