1 ;;; Continuation-passing style (CPS) intermediate language (IL)
3 ;; Copyright (C) 2014, 2015 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 ;;; A pass to renumber variables and continuation labels so that they
22 ;;; are contiguous within each function and, in the case of labels,
23 ;;; topologically sorted.
27 (define-module (language cps renumber)
28 #:use-module (ice-9 match)
29 #:use-module (srfi srfi-1)
30 #:use-module (language cps)
33 ;; Topologically sort the continuation tree starting at k0, using
34 ;; reverse post-order numbering.
35 (define (sort-conts k0 conts new-k0 path-lengths)
38 (define (maybe-visit k)
39 (let ((entry (vector-ref conts k)))
40 ;; Visit the successor if it has not been
42 (when (and entry (not (exact-integer? entry)))
45 (let ((cont (vector-ref conts k)))
46 ;; Clear the cont table entry to mark this continuation as
48 (vector-set! conts k #f)
51 (($ $kargs names syms body)
54 (($ $letk conts body) (lp body))
55 (($ $letrec names syms funs body) (lp body))
56 (($ $continue k src exp)
58 (($ $prompt escape? tag handler)
62 ;; Visit the successor with the shortest path length
63 ;; to the tail first, so that if the branches are
64 ;; unsorted, the longer path length will appear
65 ;; first. This will move a loop exit out of a loop.
66 (let ((k-len (vector-ref path-lengths k))
67 (kt-len (vector-ref path-lengths kt)))
72 ;; If the path lengths are the
73 ;; same, preserve original order
74 ;; to avoid squirreliness.
75 (and (= k-len kt-len) (< kt k)))
76 (if k-len #f (< kt k)))
84 (($ $kreceive arity k) (maybe-visit k))
85 (($ $kclause arity ($ $cont kbody) alt)
87 (($ $cont kalt) (maybe-visit kalt))
90 (($ $kfun src meta self tail clause)
92 (($ $cont kclause) (maybe-visit kclause))
96 ;; Chain this label to the label that will follow it in the sort
97 ;; order, and record this label as the new head of the order.
98 (vector-set! conts k next)
101 ;; Finally traverse the label chain, giving each label its final
103 (let lp ((n new-k0) (head next))
106 (let ((next (vector-ref conts head)))
107 (vector-set! conts head n)
108 (lp (1+ n) next))))))
110 (define (compute-tail-path-lengths preds ktail path-lengths)
111 (let visit ((k ktail) (length-in 0))
112 (let ((length (vector-ref path-lengths k)))
113 (unless (and length (<= length length-in))
114 (vector-set! path-lengths k length-in)
115 (let lp ((preds (vector-ref preds k)))
119 (visit pred (1+ length-in))
122 (define (compute-new-labels-and-vars fun)
123 (call-with-values (lambda () (compute-max-label-and-var fun))
124 (lambda (max-label max-var)
125 (let ((labels (make-vector (1+ max-label) #f))
127 (vars (make-vector (1+ max-var) #f))
129 (preds (make-vector (1+ max-label) '()))
130 (path-lengths (make-vector (1+ max-label) #f)))
131 (define (add-predecessor! pred succ)
132 (vector-set! preds succ (cons pred (vector-ref preds succ))))
133 (define (rename! var)
134 (vector-set! vars var next-var)
135 (set! next-var (1+ next-var)))
137 (define (collect-conts fun)
138 (define (visit-cont cont)
140 (($ $cont label cont)
141 (vector-set! labels label cont)
143 (($ $kargs names vars body)
144 (visit-term body label))
145 (($ $kfun src meta self tail clause)
149 (add-predecessor! label kclause)
152 (($ $kclause arity (and body ($ $cont kbody)) alternate)
153 (add-predecessor! label kbody)
157 (add-predecessor! label kalt)
158 (visit-cont alternate))
160 (($ $kreceive arity kargs)
161 (add-predecessor! label kargs))
163 (define (visit-term term label)
165 (($ $letk conts body)
166 (let lp ((conts conts))
167 (unless (null? conts)
168 (visit-cont (car conts))
170 (visit-term body label))
171 (($ $letrec names syms funs body)
172 (visit-term body label))
173 (($ $continue k src exp)
174 (add-predecessor! label k)
177 (add-predecessor! label kt))
178 (($ $prompt escape? tag handler)
179 (add-predecessor! label handler))
183 (define (compute-names-in-fun fun)
185 (define (visit-cont cont)
187 (($ $cont label cont)
188 (let ((reachable? (exact-integer? (vector-ref labels label))))
189 ;; This cont is reachable if it was given a number.
190 ;; Otherwise the cont table entry still contains the
191 ;; cont itself; clear it out to indicate that the cont
192 ;; should not be residualized.
194 (vector-set! labels label #f))
196 (($ $kargs names vars body)
198 (for-each rename! vars))
199 (visit-term body reachable?))
200 (($ $kfun src meta self tail clause)
201 (unless reachable? (error "entry should be reachable"))
205 (visit-cont clause)))
206 (($ $kclause arity body alternate)
207 (unless reachable? (error "clause should be reachable"))
210 (visit-cont alternate)))
213 ;; It's possible for the tail to be unreachable,
214 ;; if all paths contify to infinite loops. Make
215 ;; sure we mark as reachable.
216 (vector-set! labels label next-label)
217 (set! next-label (1+ next-label))))
220 (define (visit-term term reachable?)
222 (($ $letk conts body)
223 (for-each visit-cont conts)
224 (visit-term body reachable?))
225 (($ $letrec names syms funs body)
227 (for-each rename! syms)
228 (set! queue (fold (lambda (fun queue)
234 (visit-term body reachable?))
235 (($ $continue k src ($ $fun free body))
237 (set! queue (cons body queue))))
241 (($ $cont kfun ($ $kfun src meta self ($ $cont ktail)))
243 (compute-tail-path-lengths preds ktail path-lengths)
244 (set! next-label (sort-conts kfun labels next-label path-lengths))
246 (for-each compute-names-in-fun (reverse queue)))
248 (for-each compute-names-in-fun conts))))
250 (compute-names-in-fun fun)
251 (values labels vars next-label next-var)))))
253 (define (apply-renumbering term labels vars)
254 (define (relabel label) (vector-ref labels label))
255 (define (rename var) (vector-ref vars var))
256 (define (rename-kw-arity arity)
258 (($ $arity req opt rest kw aok?)
259 (make-$arity req opt rest
262 (list kw kw-name (rename kw-var))))
265 (define (must-visit-cont cont)
266 (or (visit-cont cont)
267 (error "internal error -- failed to visit cont")))
268 (define (visit-conts conts)
275 (cons cont (visit-conts conts))))
276 (else (visit-conts conts))))))
277 (define (visit-cont cont)
279 (($ $cont label cont)
280 (let ((label (relabel label)))
283 (rewrite-cps-cont cont
284 (($ $kargs names vars body)
285 (label ($kargs names (map rename vars) ,(visit-term body))))
286 (($ $kfun src meta self tail clause)
288 ($kfun src meta (rename self) ,(must-visit-cont tail)
289 ,(and clause (must-visit-cont clause)))))
292 (($ $kclause arity body alternate)
294 ($kclause ,(rename-kw-arity arity) ,(must-visit-cont body)
295 ,(and alternate (must-visit-cont alternate)))))
296 (($ $kreceive ($ $arity req () rest () #f) kargs)
297 (label ($kreceive req rest (relabel kargs))))))))))
298 (define (visit-term term)
299 (rewrite-cps-term term
300 (($ $letk conts body)
301 ,(match (visit-conts conts)
302 (() (visit-term body))
303 (conts (build-cps-term ($letk ,conts ,(visit-term body))))))
304 (($ $letrec names vars funs body)
305 ($letrec names (map rename vars) (map visit-fun funs)
307 (($ $continue k src exp)
308 ($continue (relabel k) src ,(visit-exp exp)))))
309 (define (visit-exp exp)
311 ((or ($ $const) ($ $prim))
313 (($ $closure k nfree)
314 (build-cps-exp ($closure (relabel k) nfree)))
318 (let ((args (map rename args)))
319 (build-cps-exp ($values args))))
321 (let ((args (map rename args)))
322 (build-cps-exp ($call (rename proc) args))))
323 (($ $callk k proc args)
324 (let ((args (map rename args)))
325 (build-cps-exp ($callk (relabel k) (rename proc) args))))
327 (build-cps-exp ($branch (relabel kt) ,(visit-exp exp))))
328 (($ $primcall name args)
329 (let ((args (map rename args)))
330 (build-cps-exp ($primcall name args))))
331 (($ $prompt escape? tag handler)
333 ($prompt escape? (rename tag) (relabel handler))))))
334 (define (visit-fun fun)
337 ($fun (map rename free) ,(must-visit-cont body)))))
341 (must-visit-cont term))
344 ($program ,(map must-visit-cont conts))))))
346 (define (renumber term)
347 (call-with-values (lambda () (compute-new-labels-and-vars term))
348 (lambda (labels vars nlabels nvars)
349 (values (apply-renumbering term labels vars) nlabels nvars))))