1 ;;; Tree-IL partial evaluator
3 ;; Copyright (C) 2011 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
19 (define-module (language tree-il peval)
20 #:use-module (language tree-il)
21 #:use-module (language tree-il primitives)
22 #:use-module (ice-9 vlist)
23 #:use-module (ice-9 match)
24 #:use-module (srfi srfi-1)
25 #:use-module (srfi srfi-9)
26 #:use-module (srfi srfi-11)
27 #:use-module (srfi srfi-26)
31 ;;; Partial evaluation is Guile's most important source-to-source
32 ;;; optimization pass. It performs copy propagation, dead code
33 ;;; elimination, inlining, and constant folding, all while preserving
34 ;;; the order of effects in the residual program.
36 ;;; For more on partial evaluation, see William Cook’s excellent
37 ;;; tutorial on partial evaluation at DSL 2011, called “Build your own
38 ;;; partial evaluator in 90 minutes”[0].
40 ;;; Our implementation of this algorithm was heavily influenced by
41 ;;; Waddell and Dybvig's paper, "Fast and Effective Procedure Inlining",
42 ;;; IU CS Dept. TR 484.
44 ;;; [0] http://www.cs.utexas.edu/~wcook/tutorial/.
47 ;; First, some helpers.
49 (define-syntax *logging* (identifier-syntax #f))
51 ;; For efficiency we define *logging* to inline to #f, so that the call
52 ;; to log* gets optimized out. If you want to log, uncomment these
55 ;; (define %logging #f)
56 ;; (define-syntax *logging* (identifier-syntax %logging))
58 ;; Then you can change %logging at runtime.
64 (or (eq? *logging* #t)
65 (memq 'event *logging*)))
66 (log* 'event arg ...)))))
68 (define (log* event . args)
69 (let ((pp (module-ref (resolve-interface '(ice-9 pretty-print))
71 (pp `(log ,event . ,args))
75 (define-syntax-rule (let/ec k e e* ...)
76 (let ((tag (make-prompt-tag)))
80 (let ((k (lambda args (apply abort-to-prompt tag args))))
82 (lambda (_ res) res))))
84 (define (tree-il-any proc exp)
86 (tree-il-fold (lambda (exp res)
87 (let ((res (proc exp)))
90 (let ((res (proc exp)))
95 (define (vlist-any proc vlist)
96 (let ((len (vlist-length vlist)))
99 (or (proc (vlist-ref vlist i))
102 ;; Peval will do a one-pass analysis on the source program to determine
103 ;; the set of assigned lexicals, and to identify unreferenced and
104 ;; singly-referenced lexicals.
106 ;; If peval introduces more code, via copy-propagation, it will need to
107 ;; run `build-var-table' on the new code to add to make sure it can find
108 ;; a <var> for each gensym bound in the program.
110 (define-record-type <var>
111 (make-var name gensym refcount set?)
115 (refcount var-refcount set-var-refcount!)
116 (set? var-set? set-var-set?!))
118 (define* (build-var-table exp #:optional (table vlist-null))
122 (($ <lexical-ref> src name gensym)
123 (let ((var (vhash-assq gensym res)))
126 (set-var-refcount! (cdr var) (1+ (var-refcount (cdr var))))
128 (vhash-consq gensym (make-var name gensym 1 #f) res))))
132 (($ <lexical-set> src name gensym exp)
133 (let ((var (vhash-assq gensym res)))
136 (set-var-set?! (cdr var) #t)
138 (vhash-consq gensym (make-var name gensym 0 #t) res))))
140 (lambda (exp res) res)
143 ;; Counters are data structures used to limit the effort that peval
144 ;; spends on particular inlining attempts. Each call site in the source
145 ;; program is allocated some amount of effort. If peval exceeds the
146 ;; effort counter while attempting to inline a call site, it aborts the
147 ;; inlining attempt and residualizes a call instead.
149 ;; As there is a fixed number of call sites, that makes `peval' O(N) in
150 ;; the number of call sites in the source program.
152 ;; Counters should limit the size of the residual program as well, but
153 ;; currently this is not implemented.
155 ;; At the top level, before seeing any peval call, there is no counter,
156 ;; because inlining will terminate as there is no recursion. When peval
157 ;; sees a call at the top level, it will make a new counter, allocating
158 ;; it some amount of effort and size.
160 ;; This top-level effort counter effectively "prints money". Within a
161 ;; toplevel counter, no more effort is printed ex nihilo; for a nested
162 ;; inlining attempt to proceed, effort must be transferred from the
163 ;; toplevel counter to the nested counter.
165 ;; Via `data' and `prev', counters form a linked list, terminating in a
166 ;; toplevel counter. In practice `data' will be the a pointer to the
167 ;; source expression of the procedure being inlined.
169 ;; In this way peval can detect a recursive inlining attempt, by walking
170 ;; back on the `prev' links looking for matching `data'. Recursive
171 ;; counters receive a more limited effort allocation, as we don't want
172 ;; to spend all of the effort for a toplevel inlining site on loops.
173 ;; Also, recursive counters don't need a prompt at each inlining site:
174 ;; either the call chain folds entirely, or it will be residualized at
175 ;; its original call.
177 (define-record-type <counter>
178 (%make-counter effort size continuation recursive? data prev)
180 (effort effort-counter)
182 (continuation counter-continuation)
183 (recursive? counter-recursive?)
187 (define (abort-counter c)
188 ((counter-continuation c)))
190 (define (record-effort! c)
191 (let ((e (effort-counter c)))
192 (if (zero? (variable-ref e))
194 (variable-set! e (1- (variable-ref e))))))
196 (define (record-size! c)
197 (let ((s (size-counter c)))
198 (if (zero? (variable-ref s))
200 (variable-set! s (1- (variable-ref s))))))
202 (define (find-counter data counter)
204 (if (eq? data (counter-data counter))
206 (find-counter data (counter-prev counter)))))
208 (define* (transfer! from to #:optional
209 (effort (variable-ref (effort-counter from)))
210 (size (variable-ref (size-counter from))))
211 (define (transfer-counter! from-v to-v amount)
212 (let* ((from-balance (variable-ref from-v))
213 (to-balance (variable-ref to-v))
214 (amount (min amount from-balance)))
215 (variable-set! from-v (- from-balance amount))
216 (variable-set! to-v (+ to-balance amount))))
218 (transfer-counter! (effort-counter from) (effort-counter to) effort)
219 (transfer-counter! (size-counter from) (size-counter to) size))
221 (define (make-top-counter effort-limit size-limit continuation data)
222 (%make-counter (make-variable effort-limit)
223 (make-variable size-limit)
229 (define (make-nested-counter continuation data current)
230 (let ((c (%make-counter (make-variable 0)
236 (transfer! current c)
239 (define (make-recursive-counter effort-limit size-limit orig current)
240 (let ((c (%make-counter (make-variable 0)
242 (counter-continuation orig)
246 (transfer! current c effort-limit size-limit)
249 (define (types-check? primitive-name args)
252 ((not pair? null? list? symbol? vector? struct?)
256 ;; FIXME: add more cases?
259 (define (fresh-gensyms syms)
260 (map (lambda (x) (gensym (string-append (symbol->string x) " ")))
263 ;; Copy propagation of terms that bind variables, like `lambda' terms,
264 ;; will need to bind fresh variables. This procedure renames all the
265 ;; lexicals in a term.
267 (define (alpha-rename exp)
268 "Alpha-rename EXP. For any lambda in EXP, generate new symbols and
269 replace all lexical references to the former symbols with lexical
270 references to the new symbols."
271 ;; XXX: This should be factorized somehow.
273 (mapping vlist-null)) ; maps old to new gensyms
275 (($ <lambda-case> src req opt rest kw inits gensyms body alt)
276 ;; Create new symbols to replace GENSYMS and propagate them down
278 (let* ((new (fresh-gensyms
281 (if rest (list rest) '())
283 ((aok? (_ name _) ...) name)
285 (mapping (fold vhash-consq mapping gensyms new)))
286 (make-lambda-case src req opt rest
288 ((aok? (kw name old) ...)
292 (take-right new (length old)))))
294 (map (cut loop <> mapping) inits)
297 (and alt (loop alt mapping)))))
298 (($ <lexical-ref> src name gensym)
299 ;; Possibly replace GENSYM by the new gensym defined in MAPPING.
300 (let ((val (vhash-assq gensym mapping)))
302 (make-lexical-ref src name (cdr val))
304 (($ <lexical-set> src name gensym exp)
305 (let ((val (vhash-assq gensym mapping)))
306 (make-lexical-set src name (if val (cdr val) gensym)
307 (loop exp mapping))))
308 (($ <lambda> src meta body)
309 (make-lambda src meta (loop body mapping)))
310 (($ <let> src names gensyms vals body)
311 ;; As for `lambda-case' rename GENSYMS to avoid any collision.
312 (let* ((new (fresh-gensyms names))
313 (mapping (fold vhash-consq mapping gensyms new))
314 (vals (map (cut loop <> mapping) vals))
315 (body (loop body mapping)))
316 (make-let src names new vals body)))
317 (($ <letrec> src in-order? names gensyms vals body)
319 (let* ((new (fresh-gensyms names))
320 (mapping (fold vhash-consq mapping gensyms new))
321 (vals (map (cut loop <> mapping) vals))
322 (body (loop body mapping)))
323 (make-letrec src in-order? names new vals body)))
324 (($ <fix> src names gensyms vals body)
326 (let* ((new (fresh-gensyms names))
327 (mapping (fold vhash-consq mapping gensyms new))
328 (vals (map (cut loop <> mapping) vals))
329 (body (loop body mapping)))
330 (make-fix src names new vals body)))
331 (($ <let-values> src exp body)
332 (make-let-values src (loop exp mapping) (loop body mapping)))
343 (($ <toplevel-set> src name exp)
344 (make-toplevel-set src name (loop exp mapping)))
345 (($ <toplevel-define> src name exp)
346 (make-toplevel-define src name (loop exp mapping)))
347 (($ <module-set> src mod name public? exp)
348 (make-module-set src mod name public? (loop exp mapping)))
349 (($ <dynlet> src fluids vals body)
351 (map (cut loop <> mapping) fluids)
352 (map (cut loop <> mapping) vals)
353 (loop body mapping)))
354 (($ <dynwind> src winder body unwinder)
356 (loop winder mapping)
358 (loop unwinder mapping)))
359 (($ <dynref> src fluid)
360 (make-dynref src (loop fluid mapping)))
361 (($ <dynset> src fluid exp)
362 (make-dynset src (loop fluid mapping) (loop exp mapping)))
363 (($ <conditional> src condition subsequent alternate)
364 (make-conditional src
365 (loop condition mapping)
366 (loop subsequent mapping)
367 (loop alternate mapping)))
368 (($ <application> src proc args)
369 (make-application src (loop proc mapping)
370 (map (cut loop <> mapping) args)))
371 (($ <sequence> src exps)
372 (make-sequence src (map (cut loop <> mapping) exps)))
373 (($ <prompt> src tag body handler)
374 (make-prompt src (loop tag mapping) (loop body mapping)
375 (loop handler mapping)))
376 (($ <abort> src tag args tail)
377 (make-abort src (loop tag mapping) (map (cut loop <> mapping) args)
378 (loop tail mapping))))))
380 (define* (peval exp #:optional (cenv (current-module)) (env vlist-null)
382 (operator-size-limit 40)
383 (operand-size-limit 20)
384 (value-size-limit 10)
386 (recursive-effort-limit 100))
387 "Partially evaluate EXP in compilation environment CENV, with
388 top-level bindings from ENV and return the resulting expression."
390 ;; This is a simple partial evaluator. It effectively performs
391 ;; constant folding, copy propagation, dead code elimination, and
396 ;; Propagate copies across toplevel bindings, if we can prove the
397 ;; bindings to be immutable.
399 ;; Specialize lambda expressions with invariant arguments.
401 (define local-toplevel-env
402 ;; The top-level environment of the module being compiled.
404 (($ <toplevel-define> _ name)
405 (vhash-consq name #t env))
406 (($ <sequence> _ exps)
409 (($ <toplevel-define> _ name)
410 (vhash-consq name #t r))
416 (define (local-toplevel? name)
417 (vhash-assq name local-toplevel-env))
420 ;; renamed-term -> original-term
422 (define store (build-var-table exp))
424 (define (assigned-lexical? sym)
425 (let ((v (vhash-assq sym store)))
426 (and v (var-set? (cdr v)))))
428 (define (lexical-refcount sym)
429 (let ((v (vhash-assq sym store)))
430 (if v (var-refcount (cdr v)) 0)))
432 ;; ORIG has been alpha-renamed to NEW. Analyze NEW and record a link
435 (define (record-source-expression! orig new)
436 (set! store (vhash-consq new
437 (source-expression orig)
438 (build-var-table new store)))
441 ;; Find the source expression corresponding to NEW. Used to detect
442 ;; recursive inlining attempts.
444 (define (source-expression new)
445 (let ((x (vhash-assq new store)))
448 (define residual-lexical-references (make-hash-table))
450 (define (record-residual-lexical-reference! sym)
451 (hashq-set! residual-lexical-references sym #t))
453 (define (apply-primitive name args)
454 ;; todo: further optimize commutative primitives
459 (apply (module-ref the-scm-module name) args))
461 (values #t results))))
465 (define (inline-values exp src names gensyms body)
466 (let loop ((exp exp))
468 ;; Some expression types are always singly-valued.
477 ($ <lexical-set>) ; FIXME: these set! expressions
478 ($ <toplevel-set>) ; could return zero values in
479 ($ <toplevel-define>) ; the future
482 (and (= (length names) 1)
483 (make-let src names gensyms (list exp) body)))
484 (($ <application> src
485 ($ <primitive-ref> _ (? singly-valued-primitive? name)))
486 (and (= (length names) 1)
487 (make-let src names gensyms (list exp) body)))
489 ;; Statically-known number of values.
490 (($ <application> src ($ <primitive-ref> _ 'values) vals)
491 (and (= (length names) (length vals))
492 (make-let src names gensyms vals body)))
494 ;; Not going to copy code into both branches.
495 (($ <conditional>) #f)
497 ;; Bail on other applications.
498 (($ <application>) #f)
500 ;; Bail on prompt and abort.
504 ;; Propagate to tail positions.
505 (($ <let> src names gensyms vals body)
506 (let ((body (loop body)))
508 (make-let src names gensyms vals body))))
509 (($ <letrec> src in-order? names gensyms vals body)
510 (let ((body (loop body)))
512 (make-letrec src in-order? names gensyms vals body))))
513 (($ <fix> src names gensyms vals body)
514 (let ((body (loop body)))
516 (make-fix src names gensyms vals body))))
517 (($ <let-values> src exp
518 ($ <lambda-case> src2 req opt rest kw inits gensyms body #f))
519 (let ((body (loop body)))
521 (make-let-values src exp
522 (make-lambda-case src2 req opt rest kw
523 inits gensyms body #f)))))
524 (($ <dynwind> src winder body unwinder)
525 (let ((body (loop body)))
527 (make-dynwind src winder body unwinder))))
528 (($ <dynlet> src fluids vals body)
529 (let ((body (loop body)))
531 (make-dynlet src fluids vals body))))
532 (($ <sequence> src exps)
535 (let ((tail (loop tail)))
537 (make-sequence src (append head (list tail)))))))))))
539 (define (make-values src values)
541 ((single) single) ; 1 value
542 ((_ ...) ; 0, or 2 or more values
543 (make-application src (make-primitive-ref src 'values)
546 (define (constant-expression? x)
547 ;; Return true if X is constant---i.e., if it is known to have no
548 ;; effects, does not allocate storage for a mutable object, and does
549 ;; not access mutable data (like `car' or toplevel references).
555 (($ <lambda-case> _ req opt rest kw inits _ body alternate)
556 (and (every loop inits) (loop body)
557 (or (not alternate) (loop alternate))))
558 (($ <lexical-ref> _ _ gensym)
559 (not (assigned-lexical? gensym)))
560 (($ <primitive-ref>) #t)
561 (($ <conditional> _ condition subsequent alternate)
562 (and (loop condition) (loop subsequent) (loop alternate)))
563 (($ <application> _ ($ <primitive-ref> _ name) args)
564 (and (effect-free-primitive? name)
565 (not (constructor-primitive? name))
566 (types-check? name args)
568 (($ <application> _ ($ <lambda> _ _ body) args)
569 (and (loop body) (every loop args)))
570 (($ <sequence> _ exps)
572 (($ <let> _ _ _ vals body)
573 (and (every loop vals) (loop body)))
574 (($ <letrec> _ _ _ _ vals body)
575 (and (every loop vals) (loop body)))
576 (($ <fix> _ _ _ vals body)
577 (and (every loop vals) (loop body)))
578 (($ <let-values> _ exp body)
579 (and (loop exp) (loop body)))
580 (($ <prompt> _ tag body handler)
581 (and (loop tag) (loop body) (loop handler)))
584 (define (prune-bindings names syms vals body for-effect
586 (let lp ((names names) (syms syms) (vals vals)
587 (names* '()) (syms* '()) (vals* '())
589 (match (list names syms vals)
591 (let ((body (if (null? effects)
593 (make-sequence #f (reverse (cons body effects))))))
596 (build-result (reverse names*) (reverse syms*)
597 (reverse vals*) body))))
598 (((name . names) (sym . syms) (val . vals))
599 (if (hashq-ref residual-lexical-references sym)
601 (cons name names*) (cons sym syms*) (cons val vals*)
603 (let ((effect (for-effect val)))
610 (cons effect effects)))))))))
612 (define (small-expression? x limit)
615 (lambda (x res) ; leaf
617 (lambda (x res) ; down
627 (env vlist-null) ; static environment
628 (counter #f) ; inlined call stack
629 (ctx 'value)) ; effect, value, test, operator, or operand
631 (and=> (vhash-assq var env) cdr))
633 (define (visit exp ctx)
634 (loop exp env counter ctx))
636 (define (for-value exp) (visit exp 'value))
637 (define (for-operand exp) (visit exp 'operand))
638 (define (for-test exp) (visit exp 'test))
639 (define (for-effect exp) (visit exp 'effect))
640 (define (for-tail exp) (visit exp ctx))
643 (record-effort! counter))
645 (log 'visit ctx (and=> counter effort-counter)
646 (unparse-tree-il exp))
651 ((effect) (make-void #f))
655 ((test) (make-const #f #t))
657 (($ <lexical-ref> _ _ gensym)
659 ((effect) (make-void #f))
661 (log 'begin-copy gensym)
662 (let ((val (lookup gensym)))
665 (assigned-lexical? gensym)
666 (not (constant-expression? val)))
667 ;; Don't copy-propagate through assigned variables,
668 ;; and don't reorder effects.
669 (log 'unbound-or-not-constant gensym val)
670 (record-residual-lexical-reference! gensym)
676 (primitive-ref? val))
677 ;; Always propagate simple values that cannot lead to
679 (log 'copy-simple gensym val)
681 ((= 1 (lexical-refcount gensym))
682 ;; Always propagate values referenced only once.
683 ;; There is no need to rename the bindings, as they
684 ;; are only being moved, not copied. However in
685 ;; operator context we do rename it, as that
686 ;; effectively clears out the residualized-lexical
687 ;; flags that may have been set when this value was
688 ;; visited previously as an operand.
689 (log 'copy-single gensym val)
691 ((test) (for-test val))
692 ((operator) (record-source-expression! val (alpha-rename val)))
694 ;; FIXME: do demand-driven size accounting rather than
697 ;; A pure expression in the operator position. Inline
698 ;; if it's a lambda that's small enough.
699 (if (and (lambda? val)
700 (small-expression? val operator-size-limit))
702 (log 'copy-operator gensym val)
703 (record-source-expression! val (alpha-rename val)))
705 (log 'too-big-for-operator gensym val)
706 (record-residual-lexical-reference! gensym)
709 ;; A pure expression in the operand position. Inline
710 ;; if it's small enough.
711 (if (small-expression? val operand-size-limit)
713 (log 'copy-operand gensym val)
714 (record-source-expression! val (alpha-rename val)))
716 (log 'too-big-for-operand gensym val)
717 (record-residual-lexical-reference! gensym)
720 ;; A pure expression, processed for value. Don't
721 ;; inline lambdas, because they will probably won't
722 ;; fold because we don't know the operator.
723 (if (and (small-expression? val value-size-limit)
724 (not (tree-il-any lambda? val)))
726 (log 'copy-value gensym val)
727 (record-source-expression! val (alpha-rename val)))
729 (log 'too-big-or-has-lambda gensym val)
730 (record-residual-lexical-reference! gensym)
732 (($ <lexical-set> src name gensym exp)
733 (if (zero? (lexical-refcount gensym))
734 (let ((exp (for-effect exp)))
737 (make-sequence src (list exp (make-void #f)))))
739 (record-residual-lexical-reference! gensym)
740 (make-lexical-set src name gensym (for-value exp)))))
741 (($ <let> src names gensyms vals body)
742 (let* ((vals (map for-operand vals))
744 (fold vhash-consq env gensyms vals)
749 (for-tail (make-sequence src (append vals (list body)))))
750 ((and (lexical-ref? body)
751 (memq (lexical-ref-gensym body) gensyms))
752 (let ((sym (lexical-ref-gensym body))
753 (pairs (map cons gensyms vals)))
754 ;; (let ((x foo) (y bar) ...) x) => (begin bar ... foo)
758 (append (map cdr (alist-delete sym pairs eq?))
759 (list (assq-ref pairs sym)))))))
761 ;; Only include bindings for which lexical references
762 ;; have been residualized.
763 (prune-bindings names gensyms vals body for-effect
764 (lambda (names gensyms vals body)
765 (if (null? names) (error "what!" names))
766 (make-let src names gensyms vals body)))))))
767 (($ <letrec> src in-order? names gensyms vals body)
768 ;; Things could be done more precisely when IN-ORDER? but
769 ;; it's OK not to do it---at worst we lost an optimization
771 (let* ((vals (map for-operand vals))
773 (fold vhash-consq env gensyms vals)
776 (if (and (const? body)
777 (every constant-expression? vals))
779 (prune-bindings names gensyms vals body for-effect
780 (lambda (names gensyms vals body)
781 (make-letrec src in-order?
782 names gensyms vals body))))))
783 (($ <fix> src names gensyms vals body)
784 (let* ((vals (map for-operand vals))
786 (fold vhash-consq env gensyms vals)
791 (prune-bindings names gensyms vals body for-effect
792 (lambda (names gensyms vals body)
793 (make-fix src names gensyms vals body))))))
794 (($ <let-values> lv-src producer consumer)
795 ;; Peval the producer, then try to inline the consumer into
796 ;; the producer. If that succeeds, peval again. Otherwise
797 ;; reconstruct the let-values, pevaling the consumer.
798 (let ((producer (for-value producer)))
800 (($ <lambda-case> src req #f #f #f () gensyms body #f)
802 ((inline-values producer src req gensyms body)
806 (make-let-values lv-src producer (for-tail consumer)))))
807 (($ <dynwind> src winder body unwinder)
808 (make-dynwind src (for-value winder) (for-tail body)
809 (for-value unwinder)))
810 (($ <dynlet> src fluids vals body)
811 (make-dynlet src (map for-value fluids) (map for-value vals)
813 (($ <dynref> src fluid)
814 (make-dynref src (for-value fluid)))
815 (($ <dynset> src fluid exp)
816 (make-dynset src (for-value fluid) (for-value exp)))
817 (($ <toplevel-ref> src (? effect-free-primitive? name))
818 (if (local-toplevel? name)
820 (resolve-primitives! exp cenv)))
822 ;; todo: open private local bindings.
824 (($ <module-ref> src module (? effect-free-primitive? name) #f)
825 (let ((module (false-if-exception
826 (resolve-module module #:ensure #f))))
828 (let ((var (module-variable module name)))
829 (if (eq? var (module-variable the-scm-module name))
830 (make-primitive-ref src name)
835 (($ <module-set> src mod name public? exp)
836 (make-module-set src mod name public? (for-value exp)))
837 (($ <toplevel-define> src name exp)
838 (make-toplevel-define src name (for-value exp)))
839 (($ <toplevel-set> src name exp)
840 (make-toplevel-set src name (for-value exp)))
843 ((effect) (make-void #f))
844 ((test) (make-const #f #t))
846 (($ <conditional> src condition subsequent alternate)
847 (let ((condition (for-test condition)))
848 (if (const? condition)
849 (if (const-exp condition)
850 (for-tail subsequent)
851 (for-tail alternate))
852 (make-conditional src condition
853 (for-tail subsequent)
854 (for-tail alternate)))))
855 (($ <application> src
856 ($ <primitive-ref> _ '@call-with-values)
860 ;; No optional or kwargs.
862 _ req #f rest #f () gensyms body #f)))))
863 (for-tail (make-let-values src (make-application src producer '())
866 (($ <application> src orig-proc orig-args)
867 ;; todo: augment the global env with specialized functions
868 (let ((proc (loop orig-proc env counter 'operator)))
870 (($ <primitive-ref> _ (? constructor-primitive? name))
873 (let ((res (if (eq? ctx 'effect)
875 (make-const #f #t))))
876 (match (for-value exp)
877 (($ <application> _ ($ <primitive-ref> _ 'cons) (x xs))
879 (make-sequence src (list x xs res))))
880 (($ <application> _ ($ <primitive-ref> _ 'list) elts)
882 (make-sequence src (append elts (list res)))))
883 (($ <application> _ ($ <primitive-ref> _ 'vector) elts)
885 (make-sequence src (append elts (list res)))))
886 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag) ())
888 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag)
889 (($ <const> _ (? string?))))
893 (match (cons name (map for-value orig-args))
897 (make-application src (make-primitive-ref #f 'list)
899 (($ <application> src ($ <primitive-ref> _ 'list) elts)
900 (make-application src (make-primitive-ref #f 'list)
902 (_ (make-application src proc
905 ;; FIXME: these for-tail recursions could take
906 ;; place outside an effort counter.
907 (('car ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
908 (for-tail (make-sequence src (list tail head))))
909 (('cdr ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
910 (for-tail (make-sequence src (list head tail))))
911 (('car ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
912 (for-tail (make-sequence src (append tail (list head)))))
913 (('cdr ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
914 (for-tail (make-sequence
918 src (make-primitive-ref #f 'list) tail)))))
920 (('car ($ <const> src (head . tail)))
921 (for-tail (make-const src head)))
922 (('cdr ($ <const> src (head . tail)))
923 (for-tail (make-const src tail)))
926 (make-application src proc args))))))
927 (($ <primitive-ref> _ (? effect-free-primitive? name))
928 (let ((args (map for-value orig-args)))
929 (if (every const? args) ; only simple constants
930 (let-values (((success? values)
931 (apply-primitive name
932 (map const-exp args))))
933 (log 'fold success? values exp)
936 ((effect) (make-void #f))
938 ;; Values truncation: only take the first
941 (make-const #f (car values))
942 (make-values src '())))
944 (make-values src (map (cut make-const src <>)
946 (make-application src proc args)))
948 ((and (eq? ctx 'effect) (types-check? name args))
951 (make-application src proc args))))))
953 ($ <lambda-case> _ req opt #f #f inits gensyms body #f))
954 ;; Simple case: no rest, no keyword arguments.
955 ;; todo: handle the more complex cases
956 (let* ((nargs (length orig-args))
958 (nopt (if opt (length opt) 0))
959 (key (source-expression proc)))
961 ((or (< nargs nreq) (> nargs (+ nreq nopt)))
962 ;; An error, or effecting arguments.
963 (make-application src (for-value orig-proc)
964 (map for-value orig-args)))
965 ((or (and=> (find-counter key counter) counter-recursive?)
967 ;; A recursive call, or a lambda in the operator
968 ;; position of the source expression. Process again in
970 (log 'inline-recurse key)
971 (loop (make-let src (append req (or opt '()))
974 (drop inits (- nargs nreq)))
978 ;; An integration at the top-level, the first
979 ;; recursion of a recursive procedure, or a nested
980 ;; integration of a procedure that hasn't been seen
982 (log 'inline-begin exp)
985 (log 'inline-abort exp)
986 (k (make-application src
987 (for-value orig-proc)
988 (map for-value orig-args))))
991 ;; These first two cases will transfer effort
992 ;; from the current counter into the new
994 ((find-counter key counter)
996 (make-recursive-counter recursive-effort-limit
1000 (make-nested-counter abort key counter))
1001 ;; This case opens a new account, effectively
1002 ;; printing money. It should only do so once
1003 ;; for each call site in the source program.
1005 (make-top-counter effort-limit operand-size-limit
1008 (loop (make-let src (append req (or opt '()))
1011 (drop inits (- nargs nreq)))
1013 env new-counter ctx))
1016 ;; The nested inlining attempt succeeded.
1017 ;; Deposit the unspent effort and size back
1018 ;; into the current counter.
1019 (transfer! new-counter counter))
1021 (log 'inline-end result exp)
1024 (make-application src proc
1025 (map for-value orig-args))))))
1026 (($ <lambda> src meta body)
1028 ((effect) (make-void #f))
1029 ((test) (make-const #f #t))
1032 (make-lambda src meta (for-value body)))))
1033 (($ <lambda-case> src req opt rest kw inits gensyms body alt)
1034 (make-lambda-case src req opt rest kw
1035 (map for-value inits)
1038 (and alt (for-tail alt))))
1039 (($ <sequence> src exps)
1040 (let lp ((exps exps) (effects '()))
1047 (reverse (cons (for-tail last) effects)))))
1049 (let ((head (for-effect head)))
1052 (lp (append (sequence-exps head) rest) effects))
1056 (lp rest (cons head effects)))))))))
1057 (($ <prompt> src tag body handler)
1058 (define (singly-used-definition x)
1060 ((and (lexical-ref? x)
1061 ;; Only fetch definitions with single uses.
1062 (= (lexical-refcount (lexical-ref-gensym x)) 1)
1063 (lookup (lexical-ref-gensym x)))
1064 => singly-used-definition)
1066 (match (singly-used-definition tag)
1067 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag)
1068 (or () ((? constant-expression?))))
1069 ;; There is no way that an <abort> could know the tag
1070 ;; for this <prompt>, so we can elide the <prompt>
1074 (make-prompt src (for-value tag) (for-tail body)
1075 (for-value handler)))))
1076 (($ <abort> src tag args tail)
1077 (make-abort src (for-value tag) (map for-value args)
1078 (for-value tail))))))