summaryrefslogtreecommitdiff
path: root/module/language/cps/contification.scm
diff options
context:
space:
mode:
Diffstat (limited to 'module/language/cps/contification.scm')
-rw-r--r--module/language/cps/contification.scm414
1 files changed, 0 insertions, 414 deletions
diff --git a/module/language/cps/contification.scm b/module/language/cps/contification.scm
deleted file mode 100644
index 1f702310a..000000000
--- a/module/language/cps/contification.scm
+++ /dev/null
@@ -1,414 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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
-
-;;; Commentary:
-;;;
-;;; Contification is a pass that turns $fun instances into $cont
-;;; instances if all calls to the $fun return to the same continuation.
-;;; This is a more rigorous variant of our old "fixpoint labels
-;;; allocation" optimization.
-;;;
-;;; See Kennedy's "Compiling with Continuations, Continued", and Fluet
-;;; and Weeks's "Contification using Dominators".
-;;;
-;;; Code:
-
-(define-module (language cps contification)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (concatenate filter-map))
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps primitives)
- #:use-module (language bytecode)
- #:export (contify))
-
-(define (compute-contification fun)
- (let* ((dfg (compute-dfg fun))
- (scope-table (make-hash-table))
- (call-substs '())
- (cont-substs '())
- (cont-splices (make-hash-table)))
- (define (subst-call! sym arities body-ks)
- (set! call-substs (acons sym (map cons arities body-ks) call-substs)))
- (define (subst-return! old-tail new-tail)
- (set! cont-substs (acons old-tail new-tail cont-substs)))
- (define (splice-conts! scope conts)
- (for-each (match-lambda
- (($ $cont k) (hashq-set! scope-table k scope)))
- conts)
- (hashq-set! cont-splices scope
- (append conts (hashq-ref cont-splices scope '()))))
-
- (define (lookup-return-cont k)
- (match (assq-ref cont-substs k)
- (#f k)
- (k (lookup-return-cont k))))
-
- ;; If K is a continuation that binds one variable, and it has only
- ;; one predecessor, return that variable.
- (define (bound-symbol k)
- (match (lookup-cont k dfg)
- (($ $kargs (_) (sym))
- (match (lookup-predecessors k dfg)
- ((_)
- ;; K has one predecessor, the one that defined SYM.
- sym)
- (_ #f)))
- (_ #f)))
-
- (define (extract-arities clause)
- (match clause
- (($ $cont _ ($ $kclause arity body alternate))
- (cons arity (extract-arities alternate)))
- (#f '())))
- (define (extract-bodies clause)
- (match clause
- (($ $cont _ ($ $kclause arity body alternate))
- (cons body (extract-bodies alternate)))
- (#f '())))
-
- (define (contify-fun term-k sym self tail arities bodies)
- (contify-funs term-k
- (list sym) (list self) (list tail)
- (list arities) (list bodies)))
-
- ;; Given a set of mutually recursive functions bound to local
- ;; variables SYMS, with self symbols SELFS, tail continuations
- ;; TAILS, arities ARITIES, and bodies BODIES, all bound in TERM-K,
- ;; contify them if we can prove that they all return to the same
- ;; continuation. Returns a true value on success, and false
- ;; otherwise.
- (define (contify-funs term-k syms selfs tails arities bodies)
- (define (unused? sym)
- (null? (lookup-uses sym dfg)))
-
- ;; Are the given args compatible with any of the arities?
- (define (applicable? proc args)
- (let lp ((arities (assq-ref (map cons syms arities) proc)))
- (match arities
- ((($ $arity req () #f () #f) . arities)
- (or (= (length args) (length req))
- (lp arities)))
- ;; If we reached the end of the arities, fail. Also fail if
- ;; the next arity in the list has optional, keyword, or rest
- ;; arguments.
- (_ #f))))
-
- ;; If the use of PROC in continuation USE is a call to PROC that
- ;; is compatible with one of the procedure's arities, return the
- ;; target continuation. Otherwise return #f.
- (define (call-target use proc)
- (match (find-call (lookup-cont use dfg))
- (($ $continue k src ($ $call proc* args))
- (and (eq? proc proc*) (not (memq proc args)) (applicable? proc args)
- ;; Converge more quickly by resolving already-contified
- ;; call targets.
- (lookup-return-cont k)))
- (_ #f)))
-
- ;; If this set of functions is always called with one
- ;; continuation, not counting tail calls between the functions,
- ;; return that continuation.
- (define (find-common-continuation)
- (let visit-syms ((syms syms) (k #f))
- (match syms
- (() k)
- ((sym . syms)
- (let visit-uses ((uses (lookup-uses sym dfg)) (k k))
- (match uses
- (() (visit-syms syms k))
- ((use . uses)
- (and=> (call-target use sym)
- (lambda (k*)
- (cond
- ((memq k* tails) (visit-uses uses k))
- ((not k) (visit-uses uses k*))
- ((eq? k k*) (visit-uses uses k))
- (else #f)))))))))))
-
- ;; Given that the functions are called with the common
- ;; continuation K, determine the scope at which to contify the
- ;; functions. If K is in scope in the term, we go ahead and
- ;; contify them there. Otherwise the scope is inside the letrec
- ;; body, and so choose the scope in which the continuation is
- ;; defined, whose free variables are a superset of the free
- ;; variables of the functions.
- ;;
- ;; There is some slight trickiness here. Call-target already uses
- ;; the information we compute within this pass. Previous
- ;; contifications may cause functions to be contified not at their
- ;; point of definition but at their point of non-recursive use.
- ;; That will cause the scope nesting to change. (It may
- ;; effectively push a function deeper down the tree -- the second
- ;; case above, a call within the letrec body.) What if we contify
- ;; to the tail of a previously contified function? We have to
- ;; track what the new scope tree will be when asking whether K
- ;; will be bound in TERM-K's scope, not the scope tree that
- ;; existed when we started the pass.
- ;;
- ;; FIXME: Does this choose the right scope for contified let-bound
- ;; functions?
- (define (find-contification-scope k)
- (define (scope-contains? scope k)
- (let ((k-scope (or (hashq-ref scope-table k)
- (let ((k-scope (lookup-block-scope k dfg)))
- (hashq-set! scope-table k k-scope)
- k-scope))))
- (or (eq? scope k-scope)
- (and k-scope (scope-contains? scope k-scope)))))
-
- ;; Find the scope of K.
- (define (continuation-scope k)
- (or (hashq-ref scope-table k)
- (let ((scope (lookup-block-scope k dfg)))
- (hashq-set! scope-table k scope)
- scope)))
-
- (let ((k-scope (continuation-scope k)))
- (if (scope-contains? k-scope term-k)
- term-k
- (match (lookup-cont k-scope dfg)
- (($ $kfun src meta self tail clause)
- ;; K is the tail of some function. If that function
- ;; has just one clause, return that clause. Otherwise
- ;; bail.
- (match clause
- (($ $cont _ ($ $kclause arity ($ $cont kargs) #f))
- kargs)
- (_ #f)))
- (_ k-scope)))))
-
- ;; We are going to contify. Mark all SYMs for replacement in
- ;; calls, and mark the tail continuations for replacement by K.
- ;; Arrange for the continuations to be spliced into SCOPE.
- (define (enqueue-contification! k scope)
- (for-each (lambda (sym tail arities bodies)
- (match bodies
- ((($ $cont body-k) ...)
- (subst-call! sym arities body-k)))
- (subst-return! tail k))
- syms tails arities bodies)
- (splice-conts! scope (concatenate bodies))
- #t)
-
- ;; "Call me maybe"
- (and (and-map unused? selfs)
- (and=> (find-common-continuation)
- (lambda (k)
- (and=> (find-contification-scope k)
- (cut enqueue-contification! k <>))))))
-
- (define (visit-fun term)
- (match term
- (($ $fun body)
- (visit-cont body))))
- (define (visit-cont cont)
- (match cont
- (($ $cont sym ($ $kargs _ _ body))
- (visit-term body sym))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (when clause (visit-cont clause)))
- (($ $cont sym ($ $kclause arity body alternate))
- (visit-cont body)
- (when alternate (visit-cont alternate)))
- (($ $cont)
- #t)))
- (define (visit-term term term-k)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body term-k))
- (($ $continue k src exp)
- (match exp
- (($ $fun
- ($ $cont fun-k
- ($ $kfun src meta self ($ $cont tail-k ($ $ktail)) clause)))
- (if (and=> (bound-symbol k)
- (lambda (sym)
- (contify-fun term-k sym self tail-k
- (extract-arities clause)
- (extract-bodies clause))))
- (begin
- (for-each visit-cont (extract-bodies clause)))
- (visit-fun exp)))
- (($ $rec names syms funs)
- (define (split-components nsf)
- ;; FIXME: Compute strongly-connected components. Currently
- ;; we just put non-recursive functions in their own
- ;; components, and lump everything else in the remaining
- ;; component.
- (define (recursive? k)
- (or-map (cut variable-free-in? <> k dfg) syms))
- (let lp ((nsf nsf) (rec '()))
- (match nsf
- (()
- (if (null? rec)
- '()
- (list rec)))
- (((and elt (n s ($ $fun ($ $cont kfun))))
- . nsf)
- (if (recursive? kfun)
- (lp nsf (cons elt rec))
- (cons (list elt) (lp nsf rec)))))))
- (define (extract-arities+bodies clauses)
- (values (map extract-arities clauses)
- (map extract-bodies clauses)))
- (define (visit-component component)
- (match component
- (((name sym fun) ...)
- (match fun
- ((($ $fun
- ($ $cont fun-k
- ($ $kfun src meta self ($ $cont tail-k ($ $ktail))
- clause)))
- ...)
- (call-with-values (lambda () (extract-arities+bodies clause))
- (lambda (arities bodies)
- ;; Technically the procedures are created in
- ;; term-k but bound for use in k. But, there is
- ;; a tight link between term-k and k, as they
- ;; are in the same block. Mark k as the
- ;; contification scope, because that's where
- ;; they'll be used. Perhaps we can fix this
- ;; with the new CPS dialect that doesn't have
- ;; $letk.
- (if (contify-funs k sym self tail-k arities bodies)
- (for-each (cut for-each visit-cont <>) bodies)
- (for-each visit-fun fun)))))))))
- (for-each visit-component
- (split-components (map list names syms funs))))
- (_ #t)))))
-
- (visit-cont fun)
- (values call-substs cont-substs cont-splices)))
-
-(define (apply-contification fun call-substs cont-substs cont-splices)
- (define (contify-call src proc args)
- (and=> (assq-ref call-substs proc)
- (lambda (clauses)
- (let lp ((clauses clauses))
- (match clauses
- (() (error "invalid contification"))
- (((($ $arity req () #f () #f) . k) . clauses)
- (if (= (length req) (length args))
- (build-cps-term
- ($continue k src
- ($values args)))
- (lp clauses)))
- ((_ . clauses) (lp clauses)))))))
- (define (continue k src exp)
- (define (lookup-return-cont k)
- (match (assq-ref cont-substs k)
- (#f k)
- (k (lookup-return-cont k))))
- (let ((k* (lookup-return-cont k)))
- ;; We are contifying this return. It must be a call or a
- ;; primcall to values, return, or return-values.
- (if (eq? k k*)
- (build-cps-term ($continue k src ,exp))
- (rewrite-cps-term exp
- (($ $primcall 'return (val))
- ($continue k* src ($primcall 'values (val))))
- (($ $values vals)
- ($continue k* src ($primcall 'values vals)))
- (_ ($continue k* src ,exp))))))
- (define (splice-continuations term-k term)
- (match (hashq-ref cont-splices term-k)
- (#f term)
- ((cont ...)
- (let lp ((term term))
- (rewrite-cps-term term
- (($ $letk conts* body)
- ($letk ,(append conts* (filter-map visit-cont cont))
- ,body))
- (body
- ($letk ,(filter-map visit-cont cont)
- ,body)))))))
- (define (visit-fun term)
- (rewrite-cps-exp term
- (($ $fun body)
- ($fun ,(visit-cont body)))))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names syms body))
- ;; Remove bindings for functions that have been contified.
- ,(rewrite-cps-cont (filter (match-lambda
- ((name sym) (not (assq sym call-substs))))
- (map list names syms))
- (((names syms) ...)
- (label ($kargs names syms ,(visit-term body label))))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail ,(and clause (visit-cont clause)))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont)
- ,cont)))
- (define (visit-term term term-k)
- (match term
- (($ $letk conts body)
- ;; Visit the body first, so we rewrite depth-first.
- (let lp ((body (visit-term body term-k)))
- ;; Because we attach contified functions on a particular
- ;; term-k, and one term-k can correspond to an arbitrarily
- ;; nested sequence of $letk instances, normalize so that all
- ;; continuations are bound by one $letk -- guaranteeing that
- ;; they are in the same scope.
- (rewrite-cps-term body
- (($ $letk conts* body)
- ($letk ,(append conts* (filter-map visit-cont conts))
- ,body))
- (body
- ($letk ,(filter-map visit-cont conts)
- ,body)))))
- (($ $continue k src exp)
- (splice-continuations
- term-k
- (match exp
- (($ $fun
- ($ $cont fun-k ($ $kfun src meta self ($ $cont tail-k))))
- ;; If the function's tail continuation has been substituted,
- ;; that means it has been contified.
- (continue k src
- (if (assq tail-k cont-substs)
- (build-cps-exp ($values ()))
- (visit-fun exp))))
- (($ $rec names syms funs)
- (match (filter (match-lambda
- ((n s f) (not (assq s call-substs))))
- (map list names syms funs))
- (() (continue k src (build-cps-exp ($values ()))))
- (((names syms funs) ...)
- (continue k src
- (build-cps-exp
- ($rec names syms (map visit-fun funs)))))))
- (($ $call proc args)
- (or (contify-call src proc args)
- (continue k src exp)))
- (_ (continue k src exp)))))))
- (visit-cont fun))
-
-(define (contify fun)
- (call-with-values (lambda () (compute-contification fun))
- (lambda (call-substs cont-substs cont-splices)
- (if (null? call-substs)
- fun
- ;; Iterate to fixed point.
- (contify
- (apply-contification fun call-substs cont-substs cont-splices))))))