1 ;;; Continuation-passing style (CPS) intermediate language (IL)
3 ;; Copyright (C) 2013, 2014 Free Software Foundation, Inc.
5 ;;;; This library is free software; you can redistribute it and/or
6 ;;;; modify it under the terms of the GNU Lesser General Public
7 ;;;; License as published by the Free Software Foundation; either
8 ;;;; version 3 of the License, or (at your option) any later version.
10 ;;;; This library is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;;;; Lesser General Public License for more details.
15 ;;;; You should have received a copy of the GNU Lesser General Public
16 ;;;; License along with this library; if not, write to the Free Software
17 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 ;;; The fundamental lambda calculus reductions, like beta and eta
22 ;;; reduction and so on. Pretty lame currently.
26 (define-module (language cps simplify)
27 #:use-module (ice-9 match)
28 #:use-module (srfi srfi-1)
29 #:use-module (srfi srfi-11)
30 #:use-module (srfi srfi-26)
31 #:use-module (language cps)
32 #:use-module (language cps dfg)
33 #:use-module (language cps renumber)
36 (define (compute-eta-reductions fun)
37 (let ((table (make-hash-table)))
38 (define (visit-cont cont)
40 (($ $cont sym ($ $kargs names syms body))
41 (visit-term body sym syms))
42 (($ $cont sym ($ $kfun src meta self tail clause))
43 (when clause (visit-cont clause)))
44 (($ $cont sym ($ $kclause arity body alternate))
46 (when alternate (visit-cont alternate)))
47 (($ $cont sym _) #f)))
48 (define (visit-term term term-k term-args)
51 (for-each visit-cont conts)
52 (visit-term body term-k term-args))
53 (($ $letrec names syms funs body)
54 (for-each visit-fun funs)
55 (visit-term body term-k term-args))
56 (($ $continue k src ($ $values args))
57 (when (and (equal? term-args args) (not (eq? k term-k)))
58 (hashq-set! table term-k k)))
59 (($ $continue k src (and fun ($ $fun)))
61 (($ $continue k src _)
63 (define (visit-fun fun)
70 (define (eta-reduce fun)
71 (let ((table (compute-eta-reductions fun))
72 (dfg (compute-dfg fun)))
73 (define (reduce* k scope values?)
74 (match (hashq-ref table k)
77 (if (and (continuation-bound-in? k* scope dfg)
79 (match (lookup-cont k* dfg)
82 (reduce* k* scope values?)
84 (define (reduce k scope)
86 (define (reduce-values k scope)
88 (define (reduce-const k src scope const)
89 (let lp ((k k) (seen '()) (const const))
90 (match (lookup-cont k dfg)
91 (($ $kargs (_) (arg) term)
92 (match (find-call term)
93 (($ $continue k* src* ($ $values (arg*)))
96 (lp k* (cons k seen) const)))
97 (($ $continue k* src* ($ $primcall 'not (arg*)))
100 (lp k* (cons k seen) (not const))))
101 (($ $continue k* src* ($ $branch kt ($ $values (arg*))))
103 (let ((k* (if const kt k*)))
104 (and (continuation-bound-in? k* scope dfg)
106 ($continue k* src ($values ())))))))
108 (and (continuation-bound-in? k scope dfg)
110 ($continue k src ($const const)))))))
112 (define (visit-cont cont scope)
113 (rewrite-cps-cont cont
114 (($ $cont sym ($ $kargs names syms body))
115 (sym ($kargs names syms ,(visit-term body sym))))
116 (($ $cont sym ($ $kfun src meta self tail clause))
117 (sym ($kfun src meta self ,tail
118 ,(and clause (visit-cont clause sym)))))
119 (($ $cont sym ($ $kclause arity body alternate))
120 (sym ($kclause ,arity ,(visit-cont body sym)
121 ,(and alternate (visit-cont alternate sym)))))
122 (($ $cont sym ($ $kreceive ($ $arity req () rest () #f) kargs))
123 (sym ($kreceive req rest (reduce kargs scope))))))
124 (define (visit-term term scope)
125 (rewrite-cps-term term
126 (($ $letk conts body)
127 ($letk ,(map (cut visit-cont <> scope) conts)
128 ,(visit-term body scope)))
129 (($ $letrec names syms funs body)
130 ($letrec names syms (map visit-fun funs)
131 ,(visit-term body scope)))
132 (($ $continue k src ($ $values args))
133 ($continue (reduce-values k scope) src ($values args)))
134 (($ $continue k src (and fun ($ $fun)))
135 ($continue (reduce k scope) src ,(visit-fun fun)))
136 (($ $continue k src ($ $const const))
137 ,(let ((k (reduce k scope)))
138 (or (reduce-const k src scope const)
139 (build-cps-term ($continue k src ($const const))))))
140 (($ $continue k src exp)
141 ($continue (reduce k scope) src ,exp))))
142 (define (visit-fun fun)
145 ($fun free ,(visit-cont body #f)))))
146 (visit-cont fun #f)))
148 (define (compute-beta-reductions fun)
149 ;; A continuation's body can be inlined in place of a $values
150 ;; expression if the continuation is a $kargs. It should only be
151 ;; inlined if it is used only once, and not recursively.
152 (let ((var-table (make-hash-table))
153 (k-table (make-hash-table))
154 (dfg (compute-dfg fun)))
155 (define (visit-cont cont)
157 (($ $cont sym ($ $kargs names syms body))
159 (($ $cont sym ($ $kfun src meta self tail clause))
160 (when clause (visit-cont clause)))
161 (($ $cont sym ($ $kclause arity body alternate))
163 (when alternate (visit-cont alternate)))
164 (($ $cont sym (or ($ $ktail) ($ $kreceive)))
166 (define (visit-term term)
168 (($ $letk conts body)
169 (for-each visit-cont conts)
171 (($ $letrec names syms funs body)
172 (for-each visit-fun funs)
174 (($ $continue k src ($ $values args))
175 (match (lookup-cont k dfg)
176 (($ $kargs names syms body)
177 (match (lookup-predecessors k dfg)
179 ;; There is only one use, and it is this use. We assume
180 ;; it's not recursive, as there would to be some other
181 ;; use for control flow to reach this loop. Store the k
182 ;; -> body mapping in the table. Also store the
183 ;; substitutions for the variables bound by the inlined
185 (for-each (cut hashq-set! var-table <> <>) syms args)
186 (hashq-set! k-table k body))
189 (($ $continue k src (and fun ($ $fun)))
191 (($ $continue k src _)
193 (define (visit-fun fun)
198 (values var-table k-table)))
200 (define (beta-reduce fun)
201 (let-values (((var-table k-table) (compute-beta-reductions fun)))
203 (cond ((hashq-ref var-table var) => subst)
205 (define (must-visit-cont cont)
206 (or (visit-cont cont)
207 (error "continuation must not be inlined" cont)))
208 (define (visit-cont cont)
211 (and (not (hashq-ref k-table sym))
212 (rewrite-cps-cont cont
213 (($ $kargs names syms body)
214 (sym ($kargs names syms ,(visit-term body))))
215 (($ $kfun src meta self tail clause)
216 (sym ($kfun src meta self ,tail
217 ,(and clause (must-visit-cont clause)))))
218 (($ $kclause arity body alternate)
219 (sym ($kclause ,arity ,(must-visit-cont body)
220 ,(and alternate (must-visit-cont alternate)))))
223 (define (visit-term term)
225 (($ $letk conts body)
226 (match (filter-map visit-cont conts)
227 (() (visit-term body))
228 (conts (build-cps-term
229 ($letk ,conts ,(visit-term body))))))
230 (($ $letrec names syms funs body)
232 ($letrec names syms (map visit-fun funs)
233 ,(visit-term body))))
234 (($ $continue k src exp)
236 ((hashq-ref k-table k) => visit-term)
238 (build-cps-term ($continue k src ,(visit-exp exp))))))))
239 (define (visit-exp exp)
241 ((or ($ $void) ($ $const) ($ $prim)) exp)
242 (($ $fun) (visit-fun exp))
244 (let ((args (map subst args)))
245 (build-cps-exp ($call (subst proc) args))))
246 (($ $callk k proc args)
247 (let ((args (map subst args)))
248 (build-cps-exp ($callk k (subst proc) args))))
249 (($ $primcall name args)
250 (let ((args (map subst args)))
251 (build-cps-exp ($primcall name args))))
253 (let ((args (map subst args)))
254 (build-cps-exp ($values args))))
256 (build-cps-exp ($branch kt ,(visit-exp exp))))
257 (($ $prompt escape? tag handler)
258 (build-cps-exp ($prompt escape? (subst tag) handler)))))
259 (define (visit-fun fun)
262 ($fun (map subst free) ,(must-visit-cont body)))))
263 (must-visit-cont fun)))
265 ;; Rewrite the scope tree to reflect the dominator tree. Precondition:
266 ;; the fun has been renumbered, its min-label is 0, and its labels are
268 (define (redominate fun)
269 (let* ((dfg (compute-dfg fun))
270 (idoms (compute-idoms dfg 0 (dfg-label-count dfg)))
271 (doms (compute-dom-edges idoms 0)))
272 (define (visit-fun-cont cont)
273 (rewrite-cps-cont cont
274 (($ $cont label ($ $kfun src meta self tail clause))
275 (label ($kfun src meta self ,tail
276 ,(and clause (visit-fun-cont clause)))))
277 (($ $cont label ($ $kclause arity ($ $cont kbody body) alternate))
278 (label ($kclause ,arity ,(visit-cont kbody body)
279 ,(and alternate (visit-fun-cont alternate)))))))
281 (define (visit-cont label cont)
282 (rewrite-cps-cont cont
283 (($ $kargs names vars body)
284 (label ($kargs names vars ,(visit-term body label))))
287 (define (visit-exp k src exp)
288 (rewrite-cps-term exp
290 ($continue k src ($fun free ,(visit-fun-cont body))))
292 ($continue k src ,exp))))
294 (define (visit-term term label)
295 (define (visit-dom-conts label)
296 (let ((cont (lookup-cont label dfg)))
299 (($ $kargs) (list (visit-cont label cont)))
301 (cons (visit-cont label cont)
302 (visit-dom-conts* (vector-ref doms label)))))))
304 (define (visit-dom-conts* labels)
308 (append (visit-dom-conts label)
309 (visit-dom-conts* labels)))))
311 (rewrite-cps-term term
312 (($ $letk conts body)
313 ,(visit-term body label))
314 (($ $letrec names syms funs body)
315 ($letrec names syms (let lp ((funs funs))
318 ((($ $fun free body) . funs)
320 ($fun free ,(visit-fun-cont body)))
322 ,(visit-term body label)))
323 (($ $continue k src exp)
324 ,(let ((conts (visit-dom-conts* (vector-ref doms label))))
326 (visit-exp k src exp)
328 ($letk ,conts ,(visit-exp k src exp))))))))
330 (visit-fun-cont fun)))
332 (define (simplify fun)
333 ;; Renumbering prunes continuations that are made unreachable by
334 ;; eta/beta reductions.
335 (redominate (renumber (eta-reduce (beta-reduce fun)))))