123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- ;;; transformation of letrec into simpler forms
- ;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
- ;;;; This library is free software; you can redistribute it and/or
- ;;;; modify it under the terms of the GNU Lesser General Public
- ;;;; License as published by the Free Software Foundation; either
- ;;;; version 3 of the License, or (at your option) any later version.
- ;;;;
- ;;;; This library is distributed in the hope that it will be useful,
- ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; You should have received a copy of the GNU Lesser General Public
- ;;;; License along with this library; if not, write to the Free Software
- ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- (define-module (language tree-il fix-letrec)
- #:use-module (system base syntax)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (language tree-il)
- #:use-module (language tree-il primitives)
- #:export (fix-letrec!))
- ;; For a detailed discussion, see "Fixing Letrec: A Faithful Yet
- ;; Efficient Implementation of Scheme's Recursive Binding Construct", by
- ;; Oscar Waddell, Dipanwita Sarkar, and R. Kent Dybvig.
- (define fix-fold
- (make-tree-il-folder unref ref set simple lambda complex))
- (define (simple-expression? x bound-vars simple-primitive?)
- (record-case x
- ((<void>) #t)
- ((<const>) #t)
- ((<lexical-ref> gensym)
- (not (memq gensym bound-vars)))
- ((<conditional> test consequent alternate)
- (and (simple-expression? test bound-vars simple-primitive?)
- (simple-expression? consequent bound-vars simple-primitive?)
- (simple-expression? alternate bound-vars simple-primitive?)))
- ((<sequence> exps)
- (and-map (lambda (x) (simple-expression? x bound-vars simple-primitive?))
- exps))
- ((<application> proc args)
- (and (primitive-ref? proc)
- (simple-primitive? (primitive-ref-name proc))
- ;; FIXME: check arity?
- (and-map (lambda (x)
- (simple-expression? x bound-vars simple-primitive?))
- args)))
- (else #f)))
- (define (partition-vars x)
- (let-values
- (((unref ref set simple lambda* complex)
- (fix-fold x
- (lambda (x unref ref set simple lambda* complex)
- (record-case x
- ((<lexical-ref> gensym)
- (values (delq gensym unref)
- (lset-adjoin eq? ref gensym)
- set
- simple
- lambda*
- complex))
- ((<lexical-set> gensym)
- (values unref
- ref
- (lset-adjoin eq? set gensym)
- simple
- lambda*
- complex))
- ((<letrec> gensyms)
- (values (append gensyms unref)
- ref
- set
- simple
- lambda*
- complex))
- ((<let> gensyms)
- (values (append gensyms unref)
- ref
- set
- simple
- lambda*
- complex))
- (else
- (values unref ref set simple lambda* complex))))
- (lambda (x unref ref set simple lambda* complex)
- (record-case x
- ((<letrec> in-order? (orig-gensyms gensyms) vals)
- (let lp ((gensyms orig-gensyms) (vals vals)
- (s '()) (l '()) (c '()))
- (cond
- ((null? gensyms)
- ;; Unreferenced complex vars are still
- ;; complex for letrec*. We need to update
- ;; our algorithm to "Fixing letrec reloaded"
- ;; to fix this.
- (values (if in-order?
- (lset-difference eq? unref c)
- unref)
- ref
- set
- (append s simple)
- (append l lambda*)
- (append c complex)))
- ((memq (car gensyms) unref)
- ;; See above note about unref and letrec*.
- (if (and in-order?
- (not (lambda? (car vals)))
- (not (simple-expression?
- (car vals) orig-gensyms
- effect+exception-free-primitive?)))
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))
- (lp (cdr gensyms) (cdr vals)
- s l c)))
- ((memq (car gensyms) set)
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c)))
- ((lambda? (car vals))
- (lp (cdr gensyms) (cdr vals)
- s (cons (car gensyms) l) c))
- ((simple-expression?
- (car vals) orig-gensyms
- (if in-order?
- effect+exception-free-primitive?
- effect-free-primitive?))
- ;; For letrec*, we can't consider e.g. `car' to be
- ;; "simple", as it could raise an exception. Hence
- ;; effect+exception-free-primitive? above.
- (lp (cdr gensyms) (cdr vals)
- (cons (car gensyms) s) l c))
- (else
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))))))
- ((<let> (orig-gensyms gensyms) vals)
- ;; The point is to compile let-bound lambdas as
- ;; efficiently as we do letrec-bound lambdas, so
- ;; we use the same algorithm for analyzing the
- ;; gensyms. There is no problem recursing into the
- ;; bindings after the let, because all variables
- ;; have been renamed.
- (let lp ((gensyms orig-gensyms) (vals vals)
- (s '()) (l '()) (c '()))
- (cond
- ((null? gensyms)
- (values unref
- ref
- set
- (append s simple)
- (append l lambda*)
- (append c complex)))
- ((memq (car gensyms) unref)
- (lp (cdr gensyms) (cdr vals)
- s l c))
- ((memq (car gensyms) set)
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c)))
- ((and (lambda? (car vals))
- (not (memq (car gensyms) set)))
- (lp (cdr gensyms) (cdr vals)
- s (cons (car gensyms) l) c))
- ;; There is no difference between simple and
- ;; complex, for the purposes of let. Just lump
- ;; them all into complex.
- (else
- (lp (cdr gensyms) (cdr vals)
- s l (cons (car gensyms) c))))))
- (else
- (values unref ref set simple lambda* complex))))
- '()
- '()
- '()
- '()
- '()
- '())))
- (values unref simple lambda* complex)))
- (define (fix-letrec! x)
- (let-values (((unref simple lambda* complex) (partition-vars x)))
- (post-order!
- (lambda (x)
- (record-case x
- ;; Sets to unreferenced variables may be replaced by their
- ;; expression, called for effect.
- ((<lexical-set> gensym exp)
- (if (memq gensym unref)
- (make-sequence #f (list exp (make-void #f)))
- x))
- ((<letrec> src in-order? names gensyms vals body)
- (let ((binds (map list gensyms names vals)))
- ;; The bindings returned by this function need to appear in the same
- ;; order that they appear in the letrec.
- (define (lookup set)
- (let lp ((binds binds))
- (cond
- ((null? binds) '())
- ((memq (caar binds) set)
- (cons (car binds) (lp (cdr binds))))
- (else (lp (cdr binds))))))
- (let ((u (lookup unref))
- (s (lookup simple))
- (l (lookup lambda*))
- (c (lookup complex)))
- ;; Bind "simple" bindings, and locations for complex
- ;; bindings.
- (make-let
- src
- (append (map cadr s) (map cadr c))
- (append (map car s) (map car c))
- (append (map caddr s) (map (lambda (x) (make-void #f)) c))
- ;; Bind lambdas using the fixpoint operator.
- (make-fix
- src (map cadr l) (map car l) (map caddr l)
- (make-sequence
- src
- (append
- ;; The right-hand-sides of the unreferenced
- ;; bindings, for effect.
- (map caddr u)
- (cond
- ((null? c)
- ;; No complex bindings, just emit the body.
- (list body))
- (in-order?
- ;; For letrec*, assign complex bindings in order, then the
- ;; body.
- (append
- (map (lambda (c)
- (make-lexical-set #f (cadr c) (car c)
- (caddr c)))
- c)
- (list body)))
- (else
- ;; Otherwise for plain letrec, evaluate the the "complex"
- ;; bindings, in a `let' to indicate that order doesn't
- ;; matter, and bind to their variables.
- (list
- (let ((tmps (map (lambda (x) (gensym)) c)))
- (make-let
- #f (map cadr c) tmps (map caddr c)
- (make-sequence
- #f
- (map (lambda (x tmp)
- (make-lexical-set
- #f (cadr x) (car x)
- (make-lexical-ref #f (cadr x) tmp)))
- c tmps))))
- body))))))))))
- ((<let> src names gensyms vals body)
- (let ((binds (map list gensyms names vals)))
- (define (lookup set)
- (map (lambda (v) (assq v binds))
- (lset-intersection eq? gensyms set)))
- (let ((u (lookup unref))
- (l (lookup lambda*))
- (c (lookup complex)))
- (make-sequence
- src
- (append
- ;; unreferenced bindings, called for effect.
- (map caddr u)
- (list
- ;; unassigned lambdas use fix.
- (make-fix src (map cadr l) (map car l) (map caddr l)
- ;; and the "complex" bindings.
- (make-let src (map cadr c) (map car c) (map caddr c)
- body))))))))
-
- (else x)))
- x)))
- ;;; Local Variables:
- ;;; eval: (put 'record-case 'scheme-indent-function 1)
- ;;; End:
|