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-rule (let/ec k e e* ...)
50 (let ((tag (make-prompt-tag)))
54 (let ((k (lambda args (apply abort-to-prompt tag args))))
56 (lambda (_ res) res))))
58 (define (tree-il-any proc exp)
60 (tree-il-fold (lambda (exp res)
61 (let ((res (proc exp)))
64 (let ((res (proc exp)))
69 (define (vlist-any proc vlist)
70 (let ((len (vlist-length vlist)))
73 (or (proc (vlist-ref vlist i))
76 ;; Peval will do a one-pass analysis on the source program to determine
77 ;; the set of assigned lexicals, and to identify unreferenced and
78 ;; singly-referenced lexicals.
80 ;; If peval introduces more code, via copy-propagation, it will need to
81 ;; run `build-var-table' on the new code to add to make sure it can find
82 ;; a <var> for each gensym bound in the program.
84 (define-record-type <var>
85 (make-var name gensym refcount set?)
89 (refcount var-refcount set-var-refcount!)
90 (set? var-set? set-var-set?!))
92 (define* (build-var-table exp #:optional (table vlist-null))
96 (($ <lexical-ref> src name gensym)
97 (let ((var (vhash-assq gensym res)))
100 (set-var-refcount! (cdr var) (1+ (var-refcount (cdr var))))
102 (vhash-consq gensym (make-var name gensym 1 #f) res))))
106 (($ <lexical-set> src name gensym exp)
107 (let ((var (vhash-assq gensym res)))
110 (set-var-set?! (cdr var) #t)
112 (vhash-consq gensym (make-var name gensym 0 #t) res))))
114 (lambda (exp res) res)
117 ;; Counters are data structures used to limit the effort that peval
118 ;; spends on particular inlining attempts. Each call site in the source
119 ;; program is allocated some amount of effort. If peval exceeds the
120 ;; effort counter while attempting to inline a call site, it aborts the
121 ;; inlining attempt and residualizes a call instead.
123 ;; As there is a fixed number of call sites, that makes `peval' O(N) in
124 ;; the number of call sites in the source program.
126 ;; Counters should limit the size of the residual program as well, but
127 ;; currently this is not implemented.
129 ;; At the top level, before seeing any peval call, there is no counter,
130 ;; because inlining will terminate as there is no recursion. When peval
131 ;; sees a call at the top level, it will make a new counter, allocating
132 ;; it some amount of effort and size.
134 ;; This top-level effort counter effectively "prints money". Within a
135 ;; toplevel counter, no more effort is printed ex nihilo; for a nested
136 ;; inlining attempt to proceed, effort must be transferred from the
137 ;; toplevel counter to the nested counter.
139 ;; Via `data' and `prev', counters form a linked list, terminating in a
140 ;; toplevel counter. In practice `data' will be the a pointer to the
141 ;; source expression of the procedure being inlined.
143 ;; In this way peval can detect a recursive inlining attempt, by walking
144 ;; back on the `prev' links looking for matching `data'. Recursive
145 ;; counters receive a more limited effort allocation, as we don't want
146 ;; to spend all of the effort for a toplevel inlining site on loops.
147 ;; Also, recursive counters don't need a prompt at each inlining site:
148 ;; either the call chain folds entirely, or it will be residualized at
149 ;; its original call.
151 (define-record-type <counter>
152 (%make-counter effort size continuation recursive? data prev)
154 (effort effort-counter)
156 (continuation counter-continuation)
157 (recursive? counter-recursive?)
161 (define (abort-counter c)
162 ((counter-continuation c)))
164 (define (record-effort! c)
165 (let ((e (effort-counter c)))
166 (if (zero? (variable-ref e))
168 (variable-set! e (1- (variable-ref e))))))
170 (define (record-size! c)
171 (let ((s (size-counter c)))
172 (if (zero? (variable-ref s))
174 (variable-set! s (1- (variable-ref s))))))
176 (define (find-counter data counter)
178 (if (eq? data (counter-data counter))
180 (find-counter data (counter-prev counter)))))
182 (define* (transfer! from to #:optional
183 (effort (variable-ref (effort-counter from)))
184 (size (variable-ref (size-counter from))))
185 (define (transfer-counter! from-v to-v amount)
186 (let* ((from-balance (variable-ref from-v))
187 (to-balance (variable-ref to-v))
188 (amount (min amount from-balance)))
189 (variable-set! from-v (- from-balance amount))
190 (variable-set! to-v (+ to-balance amount))))
192 (transfer-counter! (effort-counter from) (effort-counter to) effort)
193 (transfer-counter! (size-counter from) (size-counter to) size))
195 (define (make-top-counter effort-limit size-limit continuation data)
196 (%make-counter (make-variable effort-limit)
197 (make-variable size-limit)
203 (define (make-nested-counter continuation data current)
204 (let ((c (%make-counter (make-variable 0)
210 (transfer! current c)
213 (define (make-recursive-counter effort-limit size-limit orig current)
214 (let ((c (%make-counter (make-variable 0)
216 (counter-continuation orig)
220 (transfer! current c effort-limit size-limit)
223 (define (types-check? primitive-name args)
226 ((not pair? null? list? symbol? vector? struct?)
230 ;; FIXME: add more cases?
233 (define (fresh-gensyms syms)
234 (map (lambda (x) (gensym (string-append (symbol->string x) " ")))
237 ;; Copy propagation of terms that bind variables, like `lambda' terms,
238 ;; will need to bind fresh variables. This procedure renames all the
239 ;; lexicals in a term.
241 (define (alpha-rename exp)
242 "Alpha-rename EXP. For any lambda in EXP, generate new symbols and
243 replace all lexical references to the former symbols with lexical
244 references to the new symbols."
245 ;; XXX: This should be factorized somehow.
247 (mapping vlist-null)) ; maps old to new gensyms
249 (($ <lambda-case> src req opt rest kw inits gensyms body alt)
250 ;; Create new symbols to replace GENSYMS and propagate them down
252 (let* ((new (fresh-gensyms
255 (if rest (list rest) '())
257 ((aok? (_ name _) ...) name)
259 (mapping (fold vhash-consq mapping gensyms new)))
260 (make-lambda-case src req opt rest
262 ((aok? (kw name old) ...)
266 (take-right new (length old)))))
268 (map (cut loop <> mapping) inits)
271 (and alt (loop alt mapping)))))
272 (($ <lexical-ref> src name gensym)
273 ;; Possibly replace GENSYM by the new gensym defined in MAPPING.
274 (let ((val (vhash-assq gensym mapping)))
276 (make-lexical-ref src name (cdr val))
278 (($ <lexical-set> src name gensym exp)
279 (let ((val (vhash-assq gensym mapping)))
280 (make-lexical-set src name (if val (cdr val) gensym)
281 (loop exp mapping))))
282 (($ <lambda> src meta body)
283 (make-lambda src meta (loop body mapping)))
284 (($ <let> src names gensyms vals body)
285 ;; As for `lambda-case' rename GENSYMS to avoid any collision.
286 (let* ((new (fresh-gensyms names))
287 (mapping (fold vhash-consq mapping gensyms new))
288 (vals (map (cut loop <> mapping) vals))
289 (body (loop body mapping)))
290 (make-let src names new vals body)))
291 (($ <letrec> src in-order? names gensyms vals body)
293 (let* ((new (fresh-gensyms names))
294 (mapping (fold vhash-consq mapping gensyms new))
295 (vals (map (cut loop <> mapping) vals))
296 (body (loop body mapping)))
297 (make-letrec src in-order? names new vals body)))
298 (($ <fix> src names gensyms vals body)
300 (let* ((new (fresh-gensyms names))
301 (mapping (fold vhash-consq mapping gensyms new))
302 (vals (map (cut loop <> mapping) vals))
303 (body (loop body mapping)))
304 (make-fix src names new vals body)))
305 (($ <let-values> src exp body)
306 (make-let-values src (loop exp mapping) (loop body mapping)))
317 (($ <toplevel-set> src name exp)
318 (make-toplevel-set src name (loop exp mapping)))
319 (($ <toplevel-define> src name exp)
320 (make-toplevel-define src name (loop exp mapping)))
321 (($ <module-set> src mod name public? exp)
322 (make-module-set src mod name public? (loop exp mapping)))
323 (($ <dynlet> src fluids vals body)
325 (map (cut loop <> mapping) fluids)
326 (map (cut loop <> mapping) vals)
327 (loop body mapping)))
328 (($ <dynwind> src winder body unwinder)
330 (loop winder mapping)
332 (loop unwinder mapping)))
333 (($ <dynref> src fluid)
334 (make-dynref src (loop fluid mapping)))
335 (($ <dynset> src fluid exp)
336 (make-dynset src (loop fluid mapping) (loop exp mapping)))
337 (($ <conditional> src condition subsequent alternate)
338 (make-conditional src
339 (loop condition mapping)
340 (loop subsequent mapping)
341 (loop alternate mapping)))
342 (($ <application> src proc args)
343 (make-application src (loop proc mapping)
344 (map (cut loop <> mapping) args)))
345 (($ <sequence> src exps)
346 (make-sequence src (map (cut loop <> mapping) exps)))
347 (($ <prompt> src tag body handler)
348 (make-prompt src (loop tag mapping) (loop body mapping)
349 (loop handler mapping)))
350 (($ <abort> src tag args tail)
351 (make-abort src (loop tag mapping) (map (cut loop <> mapping) args)
352 (loop tail mapping))))))
354 (define* (peval exp #:optional (cenv (current-module)) (env vlist-null)
356 (operator-size-limit 40)
357 (operand-size-limit 20)
358 (value-size-limit 10)
360 (recursive-effort-limit 100))
361 "Partially evaluate EXP in compilation environment CENV, with
362 top-level bindings from ENV and return the resulting expression."
364 ;; This is a simple partial evaluator. It effectively performs
365 ;; constant folding, copy propagation, dead code elimination, and
370 ;; Propagate copies across toplevel bindings, if we can prove the
371 ;; bindings to be immutable.
373 ;; Specialize lambda expressions with invariant arguments.
375 (define local-toplevel-env
376 ;; The top-level environment of the module being compiled.
378 (($ <toplevel-define> _ name)
379 (vhash-consq name #t env))
380 (($ <sequence> _ exps)
383 (($ <toplevel-define> _ name)
384 (vhash-consq name #t r))
390 (define (local-toplevel? name)
391 (vhash-assq name local-toplevel-env))
394 ;; renamed-term -> original-term
396 (define store (build-var-table exp))
398 (define (assigned-lexical? sym)
399 (let ((v (vhash-assq sym store)))
400 (and v (var-set? (cdr v)))))
402 (define (lexical-refcount sym)
403 (let ((v (vhash-assq sym store)))
404 (if v (var-refcount (cdr v)) 0)))
406 ;; ORIG has been alpha-renamed to NEW. Analyze NEW and record a link
409 (define (record-source-expression! orig new)
410 (set! store (vhash-consq new
411 (source-expression orig)
412 (build-var-table new store)))
415 ;; Find the source expression corresponding to NEW. Used to detect
416 ;; recursive inlining attempts.
418 (define (source-expression new)
419 (let ((x (vhash-assq new store)))
422 (define residual-lexical-references (make-hash-table))
424 (define (record-residual-lexical-reference! sym)
425 (hashq-set! residual-lexical-references sym #t))
427 (define (apply-primitive name args)
428 ;; todo: further optimize commutative primitives
433 (apply (module-ref the-scm-module name) args))
435 (values #t results))))
439 (define (inline-values exp src names gensyms body)
440 (let loop ((exp exp))
442 ;; Some expression types are always singly-valued.
451 ($ <lexical-set>) ; FIXME: these set! expressions
452 ($ <toplevel-set>) ; could return zero values in
453 ($ <toplevel-define>) ; the future
456 (and (= (length names) 1)
457 (make-let src names gensyms (list exp) body)))
458 (($ <application> src
459 ($ <primitive-ref> _ (? singly-valued-primitive? name)))
460 (and (= (length names) 1)
461 (make-let src names gensyms (list exp) body)))
463 ;; Statically-known number of values.
464 (($ <application> src ($ <primitive-ref> _ 'values) vals)
465 (and (= (length names) (length vals))
466 (make-let src names gensyms vals body)))
468 ;; Not going to copy code into both branches.
469 (($ <conditional>) #f)
471 ;; Bail on other applications.
472 (($ <application>) #f)
474 ;; Bail on prompt and abort.
478 ;; Propagate to tail positions.
479 (($ <let> src names gensyms vals body)
480 (let ((body (loop body)))
482 (make-let src names gensyms vals body))))
483 (($ <letrec> src in-order? names gensyms vals body)
484 (let ((body (loop body)))
486 (make-letrec src in-order? names gensyms vals body))))
487 (($ <fix> src names gensyms vals body)
488 (let ((body (loop body)))
490 (make-fix src names gensyms vals body))))
491 (($ <let-values> src exp
492 ($ <lambda-case> src2 req opt rest kw inits gensyms body #f))
493 (let ((body (loop body)))
495 (make-let-values src exp
496 (make-lambda-case src2 req opt rest kw
497 inits gensyms body #f)))))
498 (($ <dynwind> src winder body unwinder)
499 (let ((body (loop body)))
501 (make-dynwind src winder body unwinder))))
502 (($ <dynlet> src fluids vals body)
503 (let ((body (loop body)))
505 (make-dynlet src fluids vals body))))
506 (($ <sequence> src exps)
509 (let ((tail (loop tail)))
511 (make-sequence src (append head (list tail)))))))))))
513 (define (make-values src values)
515 ((single) single) ; 1 value
516 ((_ ...) ; 0, or 2 or more values
517 (make-application src (make-primitive-ref src 'values)
520 (define (constant-expression? x)
521 ;; Return true if X is constant---i.e., if it is known to have no
522 ;; effects, does not allocate storage for a mutable object, and does
523 ;; not access mutable data (like `car' or toplevel references).
529 (($ <lambda-case> _ req opt rest kw inits _ body alternate)
530 (and (every loop inits) (loop body) (loop alternate)))
531 (($ <lexical-ref> _ _ gensym)
532 (not (assigned-lexical? gensym)))
533 (($ <primitive-ref>) #t)
534 (($ <conditional> _ condition subsequent alternate)
535 (and (loop condition) (loop subsequent) (loop alternate)))
536 (($ <application> _ ($ <primitive-ref> _ name) args)
537 (and (effect-free-primitive? name)
538 (not (constructor-primitive? name))
539 (types-check? name args)
541 (($ <application> _ ($ <lambda> _ _ body) args)
542 (and (loop body) (every loop args)))
543 (($ <sequence> _ exps)
545 (($ <let> _ _ _ vals body)
546 (and (every loop vals) (loop body)))
547 (($ <letrec> _ _ _ _ vals body)
548 (and (every loop vals) (loop body)))
549 (($ <fix> _ _ _ vals body)
550 (and (every loop vals) (loop body)))
551 (($ <let-values> _ exp body)
552 (and (loop exp) (loop body)))
553 (($ <prompt> _ tag body handler)
554 (and (loop tag) (loop body) (loop handler)))
557 (define (prune-bindings names syms vals body for-effect
559 (let lp ((names names) (syms syms) (vals vals)
560 (names* '()) (syms* '()) (vals* '())
562 (match (list names syms vals)
564 (let ((body (if (null? effects)
566 (make-sequence #f (reverse (cons body effects))))))
569 (build-result (reverse names*) (reverse syms*)
570 (reverse vals*) body))))
571 (((name . names) (sym . syms) (val . vals))
572 (if (hashq-ref residual-lexical-references sym)
574 (cons name names*) (cons sym syms*) (cons val vals*)
576 (let ((effect (for-effect val)))
581 (cons effect effects)))))))))
583 (define (small-expression? x limit)
586 (lambda (x res) ; leaf
588 (lambda (x res) ; down
598 (env vlist-null) ; static environment
599 (counter #f) ; inlined call stack
600 (ctx 'value)) ; effect, value, test, operator, or operand
602 (and=> (vhash-assq var env) cdr))
604 (define (for-value exp)
605 (loop exp env counter 'value))
606 (define (for-operand exp)
607 (loop exp env counter 'operand))
608 (define (for-test exp)
609 (loop exp env counter 'test))
610 (define (for-effect exp)
611 (loop exp env counter 'effect))
612 (define (for-tail exp)
613 (loop exp env counter ctx))
616 (record-effort! counter))
621 ((effect) (make-void #f))
625 ((test) (make-const #f #t))
627 (($ <lexical-ref> _ _ gensym)
629 ((effect) (make-void #f))
631 (let ((val (lookup gensym)))
634 (assigned-lexical? gensym)
635 (not (constant-expression? val)))
636 ;; Don't copy-propagate through assigned variables,
637 ;; and don't reorder effects.
638 (record-residual-lexical-reference! gensym)
644 (primitive-ref? val))
645 ;; Always propagate simple values that cannot lead to
648 ((= 1 (lexical-refcount gensym))
649 ;; Always propagate values referenced only once.
650 ;; There is no need to rename the bindings, as they
651 ;; are only being moved, not copied. However in
652 ;; operator context we do rename it, as that
653 ;; effectively clears out the residualized-lexical
654 ;; flags that may have been set when this value was
655 ;; visited previously as an operand.
657 ((test) (for-test val))
658 ((operator) (record-source-expression! val (alpha-rename val)))
660 ;; FIXME: do demand-driven size accounting rather than
663 ;; A pure expression in the operator position. Inline
664 ;; if it's a lambda that's small enough.
665 (if (and (lambda? val)
666 (small-expression? val operator-size-limit))
667 (record-source-expression! val (alpha-rename val))
669 (record-residual-lexical-reference! gensym)
672 ;; A pure expression in the operand position. Inline
673 ;; if it's small enough.
674 (if (small-expression? val operand-size-limit)
675 (record-source-expression! val (alpha-rename val))
677 (record-residual-lexical-reference! gensym)
680 ;; A pure expression, processed for value. Don't
681 ;; inline lambdas, because they will probably won't
682 ;; fold because we don't know the operator.
683 (if (and (small-expression? val value-size-limit)
684 (not (tree-il-any lambda? val)))
685 (record-source-expression! val (alpha-rename val))
687 (record-residual-lexical-reference! gensym)
689 (($ <lexical-set> src name gensym exp)
690 (if (zero? (lexical-refcount gensym))
691 (let ((exp (for-effect exp)))
694 (make-sequence src (list exp (make-void #f)))))
696 (record-residual-lexical-reference! gensym)
697 (make-lexical-set src name gensym (for-value exp)))))
698 (($ <let> src names gensyms vals body)
699 (let* ((vals (map for-operand vals))
701 (fold vhash-consq env gensyms vals)
706 (for-tail (make-sequence src (append vals (list body)))))
707 ((and (lexical-ref? body)
708 (memq (lexical-ref-gensym body) gensyms))
709 (let ((sym (lexical-ref-gensym body))
710 (pairs (map cons gensyms vals)))
711 ;; (let ((x foo) (y bar) ...) x) => (begin bar ... foo)
715 (append (map cdr (alist-delete sym pairs eq?))
716 (list (assq-ref pairs sym)))))))
718 ;; Only include bindings for which lexical references
719 ;; have been residualized.
720 (prune-bindings names gensyms vals body for-effect
721 (lambda (names gensyms vals body)
722 (if (null? names) (error "what!" names))
723 (make-let src names gensyms vals body)))))))
724 (($ <letrec> src in-order? names gensyms vals body)
725 ;; Things could be done more precisely when IN-ORDER? but
726 ;; it's OK not to do it---at worst we lost an optimization
728 (let* ((vals (map for-operand vals))
730 (fold vhash-consq env gensyms vals)
733 (if (and (const? body)
734 (every constant-expression? vals))
736 (prune-bindings names gensyms vals body for-effect
737 (lambda (names gensyms vals body)
738 (make-letrec src in-order?
739 names gensyms vals body))))))
740 (($ <fix> src names gensyms vals body)
741 (let* ((vals (map for-operand vals))
743 (fold vhash-consq env gensyms vals)
748 (prune-bindings names gensyms vals body for-effect
749 (lambda (names gensyms vals body)
750 (make-fix src names gensyms vals body))))))
751 (($ <let-values> lv-src producer consumer)
752 ;; Peval the producer, then try to inline the consumer into
753 ;; the producer. If that succeeds, peval again. Otherwise
754 ;; reconstruct the let-values, pevaling the consumer.
755 (let ((producer (for-value producer)))
757 (($ <lambda-case> src req #f #f #f () gensyms body #f)
759 ((inline-values producer src req gensyms body)
763 (make-let-values lv-src producer (for-tail consumer)))))
764 (($ <dynwind> src winder body unwinder)
765 (make-dynwind src (for-value winder) (for-tail body)
766 (for-value unwinder)))
767 (($ <dynlet> src fluids vals body)
768 (make-dynlet src (map for-value fluids) (map for-value vals)
770 (($ <dynref> src fluid)
771 (make-dynref src (for-value fluid)))
772 (($ <dynset> src fluid exp)
773 (make-dynset src (for-value fluid) (for-value exp)))
774 (($ <toplevel-ref> src (? effect-free-primitive? name))
775 (if (local-toplevel? name)
777 (resolve-primitives! exp cenv)))
779 ;; todo: open private local bindings.
781 (($ <module-ref> src module (? effect-free-primitive? name) #f)
782 (let ((module (false-if-exception
783 (resolve-module module #:ensure #f))))
785 (let ((var (module-variable module name)))
786 (if (eq? var (module-variable the-scm-module name))
787 (make-primitive-ref src name)
792 (($ <module-set> src mod name public? exp)
793 (make-module-set src mod name public? (for-value exp)))
794 (($ <toplevel-define> src name exp)
795 (make-toplevel-define src name (for-value exp)))
796 (($ <toplevel-set> src name exp)
797 (make-toplevel-set src name (for-value exp)))
800 ((effect) (make-void #f))
801 ((test) (make-const #f #t))
803 (($ <conditional> src condition subsequent alternate)
804 (let ((condition (for-test condition)))
805 (if (const? condition)
806 (if (const-exp condition)
807 (for-tail subsequent)
808 (for-tail alternate))
809 (make-conditional src condition
810 (for-tail subsequent)
811 (for-tail alternate)))))
812 (($ <application> src
813 ($ <primitive-ref> _ '@call-with-values)
817 ;; No optional or kwargs.
819 _ req #f rest #f () gensyms body #f)))))
820 (for-tail (make-let-values src (make-application src producer '())
823 (($ <application> src orig-proc orig-args)
824 ;; todo: augment the global env with specialized functions
825 (let ((proc (loop orig-proc env counter 'operator)))
827 (($ <primitive-ref> _ (? constructor-primitive? name))
830 (let ((res (if (eq? ctx 'effect)
832 (make-const #f #t))))
833 (match (for-value exp)
834 (($ <application> _ ($ <primitive-ref> _ 'cons) (x xs))
836 (make-sequence src (list x xs res))))
837 (($ <application> _ ($ <primitive-ref> _ 'list) elts)
839 (make-sequence src (append elts (list res)))))
840 (($ <application> _ ($ <primitive-ref> _ 'vector) elts)
842 (make-sequence src (append elts (list res)))))
843 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag) ())
845 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag)
846 (($ <const> _ (? string?))))
850 (match (cons name (map for-value orig-args))
854 (make-application src (make-primitive-ref #f 'list)
856 (($ <application> src ($ <primitive-ref> _ 'list) elts)
857 (make-application src (make-primitive-ref #f 'list)
859 (_ (make-application src proc
862 ;; FIXME: these for-tail recursions could take
863 ;; place outside an effort counter.
864 (('car ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
865 (for-tail (make-sequence src (list tail head))))
866 (('cdr ($ <application> src ($ <primitive-ref> _ 'cons) (head tail)))
867 (for-tail (make-sequence src (list head tail))))
868 (('car ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
869 (for-tail (make-sequence src (append tail (list head)))))
870 (('cdr ($ <application> src ($ <primitive-ref> _ 'list) (head . tail)))
871 (for-tail (make-sequence
875 src (make-primitive-ref #f 'list) tail)))))
877 (('car ($ <const> src (head . tail)))
878 (for-tail (make-const src head)))
879 (('cdr ($ <const> src (head . tail)))
880 (for-tail (make-const src tail)))
883 (make-application src proc args))))))
884 (($ <primitive-ref> _ (? effect-free-primitive? name))
885 (let ((args (map for-value orig-args)))
886 (if (every const? args) ; only simple constants
887 (let-values (((success? values)
888 (apply-primitive name
889 (map const-exp args))))
892 ((effect) (make-void #f))
894 ;; Values truncation: only take the first
897 (make-const #f (car values))
898 (make-values src '())))
900 (make-values src (map (cut make-const src <>)
902 (make-application src proc args)))
904 ((and (eq? ctx 'effect) (types-check? name args))
907 (make-application src proc args))))))
909 ($ <lambda-case> _ req opt #f #f inits gensyms body #f))
910 ;; Simple case: no rest, no keyword arguments.
911 ;; todo: handle the more complex cases
912 (let* ((nargs (length orig-args))
914 (nopt (if opt (length opt) 0))
915 (key (source-expression proc)))
917 ((or (< nargs nreq) (> nargs (+ nreq nopt)))
918 ;; An error, or effecting arguments.
919 (make-application src (for-value orig-proc)
920 (map for-value orig-args)))
921 ((or (and=> (find-counter key counter) counter-recursive?)
923 ;; A recursive call, or a lambda in the operator
924 ;; position of the source expression. Process again in
926 (loop (make-let src (append req (or opt '()))
929 (drop inits (- nargs nreq)))
933 ;; An integration at the top-level, the first
934 ;; recursion of a recursive procedure, or a nested
935 ;; integration of a procedure that hasn't been seen
939 (k (make-application src
940 (for-value orig-proc)
941 (map for-value orig-args))))
944 ;; These first two cases will transfer effort
945 ;; from the current counter into the new
947 ((find-counter key counter)
949 (make-recursive-counter recursive-effort-limit
953 (make-nested-counter abort key counter))
954 ;; This case opens a new account, effectively
955 ;; printing money. It should only do so once
956 ;; for each call site in the source program.
958 (make-top-counter effort-limit operand-size-limit
961 (loop (make-let src (append req (or opt '()))
964 (drop inits (- nargs nreq)))
966 env new-counter ctx))
969 ;; The nested inlining attempt succeeded.
970 ;; Deposit the unspent effort and size back
971 ;; into the current counter.
972 (transfer! new-counter counter))
976 (make-application src proc
977 (map for-value orig-args))))))
978 (($ <lambda> src meta body)
980 ((effect) (make-void #f))
981 ((test) (make-const #f #t))
984 (make-lambda src meta (for-value body)))))
985 (($ <lambda-case> src req opt rest kw inits gensyms body alt)
986 (make-lambda-case src req opt rest kw
987 (map for-value inits)
990 (and alt (for-tail alt))))
991 (($ <sequence> src exps)
992 (let lp ((exps exps) (effects '()))
999 (reverse (cons (for-tail last) effects)))))
1001 (let ((head (for-effect head)))
1004 (lp (append (sequence-exps head) rest) effects))
1008 (lp rest (cons head effects)))))))))
1009 (($ <prompt> src tag body handler)
1010 (define (singly-used-definition x)
1012 ((and (lexical-ref? x)
1013 ;; Only fetch definitions with single uses.
1014 (= (lexical-refcount (lexical-ref-gensym x)) 1)
1015 (lookup (lexical-ref-gensym x)))
1016 => singly-used-definition)
1018 (match (singly-used-definition tag)
1019 (($ <application> _ ($ <primitive-ref> _ 'make-prompt-tag)
1020 (or () ((? constant-expression?))))
1021 ;; There is no way that an <abort> could know the tag
1022 ;; for this <prompt>, so we can elide the <prompt>
1026 (make-prompt src (for-value tag) (for-tail body)
1027 (for-value handler)))))
1028 (($ <abort> src tag args tail)
1029 (make-abort src (for-value tag) (map for-value args)
1030 (for-value tail))))))