123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- ;;; transformation of letrec into simpler forms
- ;; Copyright (C) 2009-2013,2016,2019 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 (ice-9 match)
- #:use-module (language tree-il)
- #:use-module (language tree-il effects)
- #:use-module (language cps graphs)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #: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, as well as
- ;; "Fixing Letrec (reloaded)", by Abdulaziz Ghuloum and R. Kent Dybvig.
- (define fix-fold (make-tree-il-folder))
- (define (analyze-lexicals x)
- (define referenced (make-hash-table))
- (define assigned (make-hash-table))
- ;; Functional hash sets would be nice.
- (fix-fold x
- (lambda (x)
- (record-case x
- ((<lexical-ref> gensym)
- (hashq-set! referenced gensym #t)
- (values))
- ((<lexical-set> gensym)
- (hashq-set! assigned gensym #t)
- (values))
- (else
- (values))))
- (lambda (x)
- (values)))
- (values referenced assigned))
- (define (make-seq* src head tail)
- (record-case head
- ((<lambda>) tail)
- ((<const>) tail)
- ((<lexical-ref>) tail)
- ((<void>) tail)
- (else (make-seq src head tail))))
- (define (free-variables expr cache)
- (define (adjoin elt set)
- (lset-adjoin eq? set elt))
- (define (union set1 set2)
- (lset-union eq? set1 set2))
- (define (difference set1 set2)
- (lset-difference eq? set1 set2))
- (define fix-fold (make-tree-il-folder))
- (define (recurse expr)
- (free-variables expr cache))
- (define (recurse* exprs)
- (fold (lambda (expr free)
- (union (recurse expr) free))
- '()
- exprs))
- (define (visit expr)
- (match expr
- ((or ($ <void>) ($ <const>) ($ <primitive-ref>)
- ($ <module-ref>) ($ <toplevel-ref>))
- '())
- (($ <lexical-ref> src name gensym)
- (list gensym))
- (($ <lexical-set> src name gensym exp)
- (adjoin gensym (recurse exp)))
- (($ <module-set> src mod name public? exp)
- (recurse exp))
- (($ <toplevel-set> src mod name exp)
- (recurse exp))
- (($ <toplevel-define> src mod name exp)
- (recurse exp))
- (($ <conditional> src test consequent alternate)
- (union (recurse test)
- (union (recurse consequent)
- (recurse alternate))))
- (($ <call> src proc args)
- (recurse* (cons proc args)))
- (($ <primcall> src name args)
- (recurse* args))
- (($ <seq> src head tail)
- (union (recurse head)
- (recurse tail)))
- (($ <lambda> src meta body)
- (recurse body))
- (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
- (union (difference (union (recurse* inits)
- (recurse body))
- gensyms)
- (if alternate
- (recurse alternate)
- '())))
- (($ <let> src names gensyms vals body)
- (union (recurse* vals)
- (difference (recurse body)
- gensyms)))
- (($ <letrec> src in-order? names gensyms vals body)
- (difference (union (recurse* vals)
- (recurse body))
- gensyms))
- (($ <fix> src names gensyms vals body)
- (difference (union (recurse* vals)
- (recurse body))
- gensyms))
- (($ <let-values> src exp body)
- (union (recurse exp)
- (recurse body)))
- (($ <prompt> src escape-only? tag body handler)
- (union (recurse tag)
- (union (recurse body)
- (recurse handler))))
- (($ <abort> src tag args tail)
- (union (recurse tag)
- (union (recurse* args)
- (recurse tail))))))
- (or (hashq-ref cache expr)
- (let ((res (visit expr)))
- (hashq-set! cache expr res)
- res)))
- (define (enumerate elts)
- (fold2 (lambda (x out id)
- (values (intmap-add out id x) (1+ id)))
- elts empty-intmap 0))
- (define (compute-complex id->sym id->init assigned)
- (define compute-effects
- (make-effects-analyzer (lambda (x) (hashq-ref assigned x))))
- (intmap-fold
- (lambda (id sym complex)
- (if (or (hashq-ref assigned sym)
- (let ((effects (compute-effects (intmap-ref id->init id))))
- (not (constant? (exclude-effects effects &allocation)))))
- (intset-add complex id)
- complex))
- id->sym empty-intset))
- (define (compute-sccs names syms inits in-order? fv-cache assigned)
- (define id->name (enumerate names))
- (define id->sym (enumerate syms))
- (define id->init (enumerate inits))
- (define sym->id (intmap-fold (lambda (id sym out) (acons sym id out))
- id->sym '()))
- (define (var-list->intset vars)
- (fold1 (lambda (sym out)
- (intset-add out (assq-ref sym->id sym)))
- vars empty-intset))
- (define (free-in-init init)
- (var-list->intset
- (lset-intersection eq? syms (free-variables init fv-cache))))
- (define fv-edges
- (fold2 (lambda (init fv i)
- (values
- (intmap-add fv i (free-in-init init))
- (1+ i)))
- inits empty-intmap 0))
- (define order-edges
- (if in-order?
- (let ((complex (compute-complex id->sym id->init assigned)))
- (intmap-fold (lambda (id sym out prev)
- (values
- (intmap-add out id (intset-intersect complex prev))
- (intset-add prev id)))
- id->sym empty-intmap empty-intset))
- empty-intmap))
- (define sccs
- (reverse
- (compute-sorted-strongly-connected-components
- (invert-graph (intmap-union fv-edges order-edges intset-union)))))
- (map (lambda (ids)
- (intset-fold-right (lambda (id out)
- (cons (list (intmap-ref id->name id)
- (intmap-ref id->sym id)
- (intmap-ref id->init id))
- out))
- ids '()))
- sccs))
- (define (fix-scc src binds body fv-cache referenced assigned)
- (match binds
- (((name sym init))
- ;; Case of an SCC containing just a single binding.
- (cond
- ((not (hashq-ref referenced sym))
- (make-seq* src init body))
- ((and (lambda? init) (not (hashq-ref assigned sym)))
- (make-fix src (list name) (list sym) (list init) body))
- ((memq sym (free-variables init fv-cache))
- (make-let src (list name) (list sym) (list (make-void src))
- (make-seq src
- (make-lexical-set src name sym init)
- body)))
- (else
- (make-let src (list name) (list sym) (list init)
- body))))
- (_
- (call-with-values (lambda ()
- (partition
- (lambda (bind)
- (match bind
- ((name sym init)
- (and (lambda? init)
- (not (hashq-ref assigned sym))))))
- binds))
- (lambda (l c)
- (define (bind-complex-vars body)
- (if (null? c)
- body
- (let ((inits (map (lambda (x) (make-void #f)) c)))
- (make-let src (map car c) (map cadr c) inits body))))
- (define (bind-lambdas body)
- (if (null? l)
- body
- (make-fix src (map car l) (map cadr l) (map caddr l) body)))
- (define (initialize-complex body)
- (fold-right (lambda (bind body)
- (match bind
- ((name sym init)
- (make-seq src
- (make-lexical-set src name sym init)
- body))))
- body c))
- (bind-complex-vars
- (bind-lambdas
- (initialize-complex body))))))))
- (define (fix-term src in-order? names gensyms vals body
- fv-cache referenced assigned)
- (fold-right (lambda (binds body)
- (fix-scc src binds body fv-cache referenced assigned))
- body
- (compute-sccs names gensyms vals in-order? fv-cache
- assigned)))
- (define (fix-letrec x)
- (let-values (((referenced assigned) (analyze-lexicals x)))
- (define fv-cache (make-hash-table))
- (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 (hashq-ref referenced gensym)
- x
- (make-seq* #f exp (make-void #f))))
- ((<letrec> src in-order? names gensyms vals body)
- (fix-term src in-order? names gensyms vals body
- fv-cache referenced assigned))
- ((<let> src names gensyms vals body)
- ;; Apply the same algorithm to <let> that binds <lambda>
- (if (or-map lambda? vals)
- (fix-term src #f names gensyms vals body
- fv-cache referenced assigned)
- x))
-
- (else x)))
- x)))
- ;;; Local Variables:
- ;;; eval: (put 'record-case 'scheme-indent-function 1)
- ;;; End:
|