procedures with rest arguments can get inlined
[bpt/guile.git] / test-suite / tests / peval.test
CommitLineData
de1eb420
AW
1;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
2;;;; Andy Wingo <wingo@pobox.com> --- May 2009
3;;;;
30c3dac7 4;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
de1eb420
AW
5;;;;
6;;;; This library is free software; you can redistribute it and/or
7;;;; modify it under the terms of the GNU Lesser General Public
8;;;; License as published by the Free Software Foundation; either
9;;;; version 3 of the License, or (at your option) any later version.
10;;;;
11;;;; This library is distributed in the hope that it will be useful,
12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14;;;; Lesser General Public License for more details.
15;;;;
16;;;; You should have received a copy of the GNU Lesser General Public
17;;;; License along with this library; if not, write to the Free Software
18;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20(define-module (test-suite tree-il)
21 #:use-module (test-suite lib)
22 #:use-module (system base compile)
23 #:use-module (system base pmatch)
24 #:use-module (system base message)
25 #:use-module (language tree-il)
26 #:use-module (language tree-il primitives)
27 #:use-module (language glil)
28 #:use-module (srfi srfi-13))
29
30(define peval
31 ;; The partial evaluator.
32 (@@ (language tree-il optimize) peval))
33
34(define-syntax pass-if-peval
35 (syntax-rules (resolve-primitives)
36 ((_ in pat)
37 (pass-if-peval in pat
38 (compile 'in #:from 'scheme #:to 'tree-il)))
39 ((_ resolve-primitives in pat)
40 (pass-if-peval in pat
41 (expand-primitives!
42 (resolve-primitives!
43 (compile 'in #:from 'scheme #:to 'tree-il)
44 (current-module)))))
45 ((_ in pat code)
46 (pass-if 'in
47 (let ((evaled (unparse-tree-il (peval code))))
48 (pmatch evaled
49 (pat #t)
50 (_ (pk 'peval-mismatch)
51 ((@ (ice-9 pretty-print) pretty-print)
52 'in)
53 (newline)
54 ((@ (ice-9 pretty-print) pretty-print)
55 evaled)
56 (newline)
57 ((@ (ice-9 pretty-print) pretty-print)
58 'pat)
59 (newline)
60 #f)))))))
61
62\f
63(with-test-prefix "partial evaluation"
64
65 (pass-if-peval
66 ;; First order, primitive.
67 (let ((x 1) (y 2)) (+ x y))
68 (const 3))
69
70 (pass-if-peval
71 ;; First order, thunk.
72 (let ((x 1) (y 2))
73 (let ((f (lambda () (+ x y))))
74 (f)))
75 (const 3))
76
77 (pass-if-peval resolve-primitives
78 ;; First order, let-values (requires primitive expansion for
79 ;; `call-with-values'.)
80 (let ((x 0))
81 (call-with-values
82 (lambda () (if (zero? x) (values 1 2) (values 3 4)))
83 (lambda (a b)
84 (+ a b))))
85 (const 3))
86
87 (pass-if-peval resolve-primitives
88 ;; First order, multiple values.
89 (let ((x 1) (y 2))
90 (values x y))
91 (apply (primitive values) (const 1) (const 2)))
92
93 (pass-if-peval resolve-primitives
94 ;; First order, multiple values truncated.
95 (let ((x (values 1 'a)) (y 2))
96 (values x y))
97 (apply (primitive values) (const 1) (const 2)))
98
99 (pass-if-peval resolve-primitives
100 ;; First order, multiple values truncated.
101 (or (values 1 2) 3)
102 (const 1))
103
104 (pass-if-peval
105 ;; First order, coalesced, mutability preserved.
106 (cons 0 (cons 1 (cons 2 (list 3 4 5))))
107 (apply (primitive list)
108 (const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
109
110 (pass-if-peval
111 ;; First order, coalesced, immutability preserved.
112 (cons 0 (cons 1 (cons 2 '(3 4 5))))
113 (apply (primitive cons) (const 0)
114 (apply (primitive cons) (const 1)
115 (apply (primitive cons) (const 2)
116 (const (3 4 5))))))
117
118 ;; These two tests doesn't work any more because we changed the way we
119 ;; deal with constants -- now the algorithm will see a construction as
120 ;; being bound to the lexical, so it won't propagate it. It can't
121 ;; even propagate it in the case that it is only referenced once,
122 ;; because:
123 ;;
124 ;; (let ((x (cons 1 2))) (lambda () x))
125 ;;
126 ;; is not the same as
127 ;;
128 ;; (lambda () (cons 1 2))
129 ;;
130 ;; Perhaps if we determined that not only was it only referenced once,
131 ;; it was not closed over by a lambda, then we could propagate it, and
132 ;; re-enable these two tests.
133 ;;
134 #;
135 (pass-if-peval
136 ;; First order, mutability preserved.
137 (let loop ((i 3) (r '()))
138 (if (zero? i)
139 r
140 (loop (1- i) (cons (cons i i) r))))
141 (apply (primitive list)
142 (apply (primitive cons) (const 1) (const 1))
143 (apply (primitive cons) (const 2) (const 2))
144 (apply (primitive cons) (const 3) (const 3))))
145 ;;
146 ;; See above.
147 #;
148 (pass-if-peval
149 ;; First order, evaluated.
150 (let loop ((i 7)
151 (r '()))
152 (if (<= i 0)
153 (car r)
154 (loop (1- i) (cons i r))))
155 (const 1))
156
157 ;; Instead here are tests for what happens for the above cases: they
158 ;; unroll but they don't fold.
159 (pass-if-peval
160 (let loop ((i 3) (r '()))
161 (if (zero? i)
162 r
163 (loop (1- i) (cons (cons i i) r))))
164 (let (r) (_)
165 ((apply (primitive list)
166 (apply (primitive cons) (const 3) (const 3))))
167 (let (r) (_)
168 ((apply (primitive cons)
169 (apply (primitive cons) (const 2) (const 2))
170 (lexical r _)))
171 (apply (primitive cons)
172 (apply (primitive cons) (const 1) (const 1))
173 (lexical r _)))))
174
175 ;; See above.
176 (pass-if-peval
177 (let loop ((i 4)
178 (r '()))
179 (if (<= i 0)
180 (car r)
181 (loop (1- i) (cons i r))))
182 (let (r) (_)
183 ((apply (primitive list) (const 4)))
184 (let (r) (_)
185 ((apply (primitive cons)
186 (const 3)
187 (lexical r _)))
188 (let (r) (_)
189 ((apply (primitive cons)
190 (const 2)
191 (lexical r _)))
192 (let (r) (_)
193 ((apply (primitive cons)
194 (const 1)
195 (lexical r _)))
196 (apply (primitive car)
197 (lexical r _)))))))
198
199 ;; Static sums.
200 (pass-if-peval
201 (let loop ((l '(1 2 3 4)) (sum 0))
202 (if (null? l)
203 sum
204 (loop (cdr l) (+ sum (car l)))))
205 (const 10))
206
207 (pass-if-peval resolve-primitives
208 (let ((string->chars
209 (lambda (s)
210 (define (char-at n)
211 (string-ref s n))
212 (define (len)
213 (string-length s))
214 (let loop ((i 0))
215 (if (< i (len))
216 (cons (char-at i)
217 (loop (1+ i)))
218 '())))))
219 (string->chars "yo"))
220 (apply (primitive list) (const #\y) (const #\o)))
221
222 (pass-if-peval
223 ;; Primitives in module-refs are resolved (the expansion of `pmatch'
224 ;; below leads to calls to (@@ (system base pmatch) car) and
225 ;; similar, which is what we want to be inlined.)
226 (begin
227 (use-modules (system base pmatch))
228 (pmatch '(a b c d)
229 ((a b . _)
230 #t)))
231 (begin
232 (apply . _)
233 (const #t)))
234
235 (pass-if-peval
236 ;; Mutability preserved.
237 ((lambda (x y z) (list x y z)) 1 2 3)
238 (apply (primitive list) (const 1) (const 2) (const 3)))
239
240 (pass-if-peval
241 ;; Don't propagate effect-free expressions that operate on mutable
242 ;; objects.
243 (let* ((x (list 1))
244 (y (car x)))
245 (set-car! x 0)
246 y)
247 (let (x) (_) ((apply (primitive list) (const 1)))
248 (let (y) (_) ((apply (primitive car) (lexical x _)))
249 (begin
250 (apply (toplevel set-car!) (lexical x _) (const 0))
251 (lexical y _)))))
252
253 (pass-if-peval
254 ;; Don't propagate effect-free expressions that operate on objects we
255 ;; don't know about.
256 (let ((y (car x)))
257 (set-car! x 0)
258 y)
259 (let (y) (_) ((apply (primitive car) (toplevel x)))
260 (begin
261 (apply (toplevel set-car!) (toplevel x) (const 0))
262 (lexical y _))))
263
264 (pass-if-peval
265 ;; Infinite recursion
266 ((lambda (x) (x x)) (lambda (x) (x x)))
267 (let (x) (_)
268 ((lambda _
269 (lambda-case
270 (((x) _ _ _ _ _)
271 (apply (lexical x _) (lexical x _))))))
272 (apply (lexical x _) (lexical x _))))
273
274 (pass-if-peval
275 ;; First order, aliased primitive.
276 (let* ((x *) (y (x 1 2))) y)
277 (const 2))
278
279 (pass-if-peval
280 ;; First order, shadowed primitive.
281 (begin
282 (define (+ x y) (pk x y))
283 (+ 1 2))
284 (begin
285 (define +
286 (lambda (_)
287 (lambda-case
288 (((x y) #f #f #f () (_ _))
289 (apply (toplevel pk) (lexical x _) (lexical y _))))))
290 (apply (toplevel +) (const 1) (const 2))))
291
292 (pass-if-peval
293 ;; First-order, effects preserved.
294 (let ((x 2))
295 (do-something!)
296 x)
297 (begin
298 (apply (toplevel do-something!))
299 (const 2)))
300
301 (pass-if-peval
302 ;; First order, residual bindings removed.
303 (let ((x 2) (y 3))
304 (* (+ x y) z))
305 (apply (primitive *) (const 5) (toplevel z)))
306
307 (pass-if-peval
308 ;; First order, with lambda.
309 (define (foo x)
310 (define (bar z) (* z z))
311 (+ x (bar 3)))
312 (define foo
313 (lambda (_)
314 (lambda-case
315 (((x) #f #f #f () (_))
316 (apply (primitive +) (lexical x _) (const 9)))))))
317
318 (pass-if-peval
319 ;; First order, with lambda inlined & specialized twice.
320 (let ((f (lambda (x y)
321 (+ (* x top) y)))
322 (x 2)
323 (y 3))
324 (+ (* x (f x y))
325 (f something x)))
326 (apply (primitive +)
327 (apply (primitive *)
328 (const 2)
329 (apply (primitive +) ; (f 2 3)
330 (apply (primitive *)
331 (const 2)
332 (toplevel top))
333 (const 3)))
334 (let (x) (_) ((toplevel something)) ; (f something 2)
335 ;; `something' is not const, so preserve order of
336 ;; effects with a lexical binding.
337 (apply (primitive +)
338 (apply (primitive *)
339 (lexical x _)
340 (toplevel top))
341 (const 2)))))
342
343 (pass-if-peval
344 ;; First order, with lambda inlined & specialized 3 times.
345 (let ((f (lambda (x y) (if (> x 0) y x))))
346 (+ (f -1 0)
347 (f 1 0)
348 (f -1 y)
349 (f 2 y)
350 (f z y)))
351 (apply (primitive +)
352 (const -1) ; (f -1 0)
353 (const 0) ; (f 1 0)
354 (begin (toplevel y) (const -1)) ; (f -1 y)
355 (toplevel y) ; (f 2 y)
356 (let (x y) (_ _) ((toplevel z) (toplevel y)) ; (f z y)
357 (if (apply (primitive >) (lexical x _) (const 0))
358 (lexical y _)
359 (lexical x _)))))
360
361 (pass-if-peval
362 ;; First order, conditional.
363 (let ((y 2))
364 (lambda (x)
365 (if (> y 0)
366 (display x)
367 'never-reached)))
368 (lambda ()
369 (lambda-case
370 (((x) #f #f #f () (_))
371 (apply (toplevel display) (lexical x _))))))
372
373 (pass-if-peval
374 ;; First order, recursive procedure.
375 (letrec ((fibo (lambda (n)
376 (if (<= n 1)
377 n
378 (+ (fibo (- n 1))
379 (fibo (- n 2)))))))
380 (fibo 4))
381 (const 3))
382
383 (pass-if-peval
384 ;; Don't propagate toplevel references, as intervening expressions
385 ;; could alter their bindings.
386 (let ((x top))
387 (foo)
388 x)
389 (let (x) (_) ((toplevel top))
390 (begin
391 (apply (toplevel foo))
392 (lexical x _))))
393
394 (pass-if-peval
395 ;; Higher order.
396 ((lambda (f x)
397 (f (* (car x) (cadr x))))
398 (lambda (x)
399 (+ x 1))
400 '(2 3))
401 (const 7))
402
403 (pass-if-peval
404 ;; Higher order with optional argument (default value).
405 ((lambda* (f x #:optional (y 0))
406 (+ y (f (* (car x) (cadr x)))))
407 (lambda (x)
408 (+ x 1))
409 '(2 3))
410 (const 7))
411
412 (pass-if-peval
413 ;; Higher order with optional argument (caller-supplied value).
414 ((lambda* (f x #:optional (y 0))
415 (+ y (f (* (car x) (cadr x)))))
416 (lambda (x)
417 (+ x 1))
418 '(2 3)
419 35)
420 (const 42))
421
422 (pass-if-peval
423 ;; Higher order with optional argument (side-effecting default
424 ;; value).
425 ((lambda* (f x #:optional (y (foo)))
426 (+ y (f (* (car x) (cadr x)))))
427 (lambda (x)
428 (+ x 1))
429 '(2 3))
430 (let (y) (_) ((apply (toplevel foo)))
431 (apply (primitive +) (lexical y _) (const 7))))
432
433 (pass-if-peval
434 ;; Higher order with optional argument (caller-supplied value).
435 ((lambda* (f x #:optional (y (foo)))
436 (+ y (f (* (car x) (cadr x)))))
437 (lambda (x)
438 (+ x 1))
439 '(2 3)
440 35)
441 (const 42))
442
443 (pass-if-peval
444 ;; Higher order.
445 ((lambda (f) (f x)) (lambda (x) x))
446 (toplevel x))
447
448 (pass-if-peval
449 ;; Bug reported at
450 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html>.
451 (let ((fold (lambda (f g) (f (g top)))))
452 (fold 1+ (lambda (x) x)))
453 (apply (primitive 1+) (toplevel top)))
454
455 (pass-if-peval
456 ;; Procedure not inlined when residual code contains recursive calls.
457 ;; <http://debbugs.gnu.org/9542>
458 (letrec ((fold (lambda (f x3 b null? car cdr)
459 (if (null? x3)
460 b
461 (f (car x3) (fold f (cdr x3) b null? car cdr))))))
462 (fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
463 (letrec (fold) (_) (_)
464 (apply (lexical fold _)
465 (primitive *)
466 (toplevel x)
467 (const 1)
468 (primitive zero?)
469 (lambda ()
470 (lambda-case
471 (((x1) #f #f #f () (_))
472 (lexical x1 _))))
473 (lambda ()
474 (lambda-case
475 (((x2) #f #f #f () (_))
476 (apply (primitive -) (lexical x2 _) (const 1))))))))
477
478 (pass-if "inlined lambdas are alpha-renamed"
479 ;; In this example, `make-adder' is inlined more than once; thus,
480 ;; they should use different gensyms for their arguments, because
481 ;; the various optimization passes assume uniquely-named variables.
482 ;;
483 ;; Bug reported at
484 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
485 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
486 (pmatch (unparse-tree-il
487 (peval (compile
488 '(let ((make-adder
489 (lambda (x) (lambda (y) (+ x y)))))
490 (cons (make-adder 1) (make-adder 2)))
491 #:to 'tree-il)))
492 ((apply (primitive cons)
493 (lambda ()
494 (lambda-case
495 (((y) #f #f #f () (,gensym1))
496 (apply (primitive +)
497 (const 1)
498 (lexical y ,ref1)))))
499 (lambda ()
500 (lambda-case
501 (((y) #f #f #f () (,gensym2))
502 (apply (primitive +)
503 (const 2)
504 (lexical y ,ref2))))))
505 (and (eq? gensym1 ref1)
506 (eq? gensym2 ref2)
507 (not (eq? gensym1 gensym2))))
508 (_ #f)))
509
510 (pass-if-peval
511 ;; Unused letrec bindings are pruned.
512 (letrec ((a (lambda () (b)))
513 (b (lambda () (a)))
514 (c (lambda (x) x)))
515 (c 10))
516 (const 10))
517
518 (pass-if-peval
519 ;; Unused letrec bindings are pruned.
520 (letrec ((a (foo!))
521 (b (lambda () (a)))
522 (c (lambda (x) x)))
523 (c 10))
524 (begin (apply (toplevel foo!))
525 (const 10)))
526
527 (pass-if-peval
528 ;; Higher order, mutually recursive procedures.
529 (letrec ((even? (lambda (x)
530 (or (= 0 x)
531 (odd? (- x 1)))))
532 (odd? (lambda (x)
533 (not (even? x)))))
534 (and (even? 4) (odd? 7)))
535 (const #t))
536
537 (pass-if-peval
538 ;; Memv with constants.
539 (memv 1 '(3 2 1))
540 (const '(1)))
541
542 (pass-if-peval
543 ;; Memv with non-constant list. It could fold but doesn't
544 ;; currently.
545 (memv 1 (list 3 2 1))
546 (apply (primitive memv)
547 (const 1)
548 (apply (primitive list) (const 3) (const 2) (const 1))))
549
550 (pass-if-peval
551 ;; Memv with non-constant key, constant list, test context
552 (case foo
553 ((3 2 1) 'a)
554 (else 'b))
555 (let (key) (_) ((toplevel foo))
556 (if (if (apply (primitive eqv?) (lexical key _) (const 3))
557 (const #t)
558 (if (apply (primitive eqv?) (lexical key _) (const 2))
559 (const #t)
560 (apply (primitive eqv?) (lexical key _) (const 1))))
561 (const a)
562 (const b))))
563
564 (pass-if-peval
565 ;; Memv with non-constant key, empty list, test context. Currently
566 ;; doesn't fold entirely.
567 (case foo
568 (() 'a)
569 (else 'b))
570 (begin (toplevel foo) (const b)))
571
572 ;;
573 ;; Below are cases where constant propagation should bail out.
574 ;;
575
576 (pass-if-peval
577 ;; Non-constant lexical is not propagated.
578 (let ((v (make-vector 6 #f)))
579 (lambda (n)
580 (vector-set! v n n)))
581 (let (v) (_)
582 ((apply (toplevel make-vector) (const 6) (const #f)))
583 (lambda ()
584 (lambda-case
585 (((n) #f #f #f () (_))
586 (apply (toplevel vector-set!)
587 (lexical v _) (lexical n _) (lexical n _)))))))
588
589 (pass-if-peval
590 ;; Mutable lexical is not propagated.
591 (let ((v (vector 1 2 3)))
592 (lambda ()
593 v))
594 (let (v) (_)
595 ((apply (primitive vector) (const 1) (const 2) (const 3)))
596 (lambda ()
597 (lambda-case
598 ((() #f #f #f () ())
599 (lexical v _))))))
600
601 (pass-if-peval
602 ;; Lexical that is not provably pure is not inlined nor propagated.
603 (let* ((x (if (> p q) (frob!) (display 'chbouib)))
604 (y (* x 2)))
605 (+ x x y))
606 (let (x) (_) ((if (apply (primitive >) (toplevel p) (toplevel q))
607 (apply (toplevel frob!))
608 (apply (toplevel display) (const chbouib))))
609 (let (y) (_) ((apply (primitive *) (lexical x _) (const 2)))
610 (apply (primitive +)
611 (lexical x _) (lexical x _) (lexical y _)))))
612
613 (pass-if-peval
614 ;; Non-constant arguments not propagated to lambdas.
615 ((lambda (x y z)
616 (vector-set! x 0 0)
617 (set-car! y 0)
618 (set-cdr! z '()))
619 (vector 1 2 3)
620 (make-list 10)
621 (list 1 2 3))
622 (let (x y z) (_ _ _)
623 ((apply (primitive vector) (const 1) (const 2) (const 3))
624 (apply (toplevel make-list) (const 10))
625 (apply (primitive list) (const 1) (const 2) (const 3)))
626 (begin
627 (apply (toplevel vector-set!)
628 (lexical x _) (const 0) (const 0))
629 (apply (toplevel set-car!)
630 (lexical y _) (const 0))
631 (apply (toplevel set-cdr!)
632 (lexical z _) (const ())))))
633
634 (pass-if-peval
635 (let ((foo top-foo) (bar top-bar))
636 (let* ((g (lambda (x y) (+ x y)))
637 (f (lambda (g x) (g x x))))
638 (+ (f g foo) (f g bar))))
639 (let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
640 (apply (primitive +)
641 (apply (primitive +) (lexical foo _) (lexical foo _))
642 (apply (primitive +) (lexical bar _) (lexical bar _)))))
643
644 (pass-if-peval
645 ;; Fresh objects are not turned into constants, nor are constants
646 ;; turned into fresh objects.
647 (let* ((c '(2 3))
648 (x (cons 1 c))
649 (y (cons 0 x)))
650 y)
651 (let (x) (_) ((apply (primitive cons) (const 1) (const (2 3))))
652 (apply (primitive cons) (const 0) (lexical x _))))
653
654 (pass-if-peval
655 ;; Bindings mutated.
656 (let ((x 2))
657 (set! x 3)
658 x)
659 (let (x) (_) ((const 2))
660 (begin
661 (set! (lexical x _) (const 3))
662 (lexical x _))))
663
664 (pass-if-peval
665 ;; Bindings mutated.
666 (letrec ((x 0)
667 (f (lambda ()
668 (set! x (+ 1 x))
669 x)))
670 (frob f) ; may mutate `x'
671 x)
672 (letrec (x) (_) ((const 0))
673 (begin
674 (apply (toplevel frob) (lambda _ _))
675 (lexical x _))))
676
677 (pass-if-peval
678 ;; Bindings mutated.
679 (letrec ((f (lambda (x)
680 (set! f (lambda (_) x))
681 x)))
682 (f 2))
683 (letrec _ . _))
684
685 (pass-if-peval
686 ;; Bindings possibly mutated.
687 (let ((x (make-foo)))
688 (frob! x) ; may mutate `x'
689 x)
690 (let (x) (_) ((apply (toplevel make-foo)))
691 (begin
692 (apply (toplevel frob!) (lexical x _))
693 (lexical x _))))
694
695 (pass-if-peval
696 ;; Inlining stops at recursive calls with dynamic arguments.
697 (let loop ((x x))
698 (if (< x 0) x (loop (1- x))))
699 (letrec (loop) (_) ((lambda (_)
700 (lambda-case
701 (((x) #f #f #f () (_))
702 (if _ _
703 (apply (lexical loop _)
704 (apply (primitive 1-)
705 (lexical x _))))))))
706 (apply (lexical loop _) (toplevel x))))
707
708 (pass-if-peval
709 ;; Recursion on the 2nd argument is fully evaluated.
710 (let ((x (top)))
711 (let loop ((x x) (y 10))
712 (if (> y 0)
713 (loop x (1- y))
714 (foo x y))))
715 (let (x) (_) ((apply (toplevel top)))
716 (apply (toplevel foo) (lexical x _) (const 0))))
717
718 (pass-if-peval
719 ;; Inlining aborted when residual code contains recursive calls.
720 ;;
721 ;; <http://debbugs.gnu.org/9542>
722 (let loop ((x x) (y 0))
723 (if (> y 0)
724 (loop (1- x) (1- y))
725 (if (< x 0)
726 x
727 (loop (1+ x) (1+ y)))))
728 (letrec (loop) (_) ((lambda (_)
729 (lambda-case
730 (((x y) #f #f #f () (_ _))
731 (if (apply (primitive >)
732 (lexical y _) (const 0))
733 _ _)))))
734 (apply (lexical loop _) (toplevel x) (const 0))))
735
736 (pass-if-peval
737 ;; Infinite recursion: `peval' gives up and leaves it as is.
738 (letrec ((f (lambda (x) (g (1- x))))
739 (g (lambda (x) (h (1+ x))))
740 (h (lambda (x) (f x))))
741 (f 0))
742 (letrec _ . _))
743
744 (pass-if-peval
745 ;; Infinite recursion: all the arguments to `loop' are static, but
746 ;; unrolling it would lead `peval' to enter an infinite loop.
747 (let loop ((x 0))
748 (and (< x top)
749 (loop (1+ x))))
750 (letrec (loop) (_) ((lambda . _))
751 (apply (lexical loop _) (const 0))))
752
753 (pass-if-peval
754 ;; This test checks that the `start' binding is indeed residualized.
755 ;; See the `referenced?' procedure in peval's `prune-bindings'.
756 (let ((pos 0))
de1eb420 757 (let ((here (let ((start pos)) (lambda () start))))
1cd63115 758 (set! pos 1) ;; Cause references to `pos' to residualize.
de1eb420
AW
759 (here)))
760 (let (pos) (_) ((const 0))
1cd63115
AW
761 (let (here) (_) (_)
762 (begin
763 (set! (lexical pos _) (const 1))
de1eb420
AW
764 (apply (lexical here _))))))
765
766 (pass-if-peval
767 ;; FIXME: should this one residualize the binding?
768 (letrec ((a a))
769 1)
770 (const 1))
771
772 (pass-if-peval
773 ;; This is a fun one for peval to handle.
774 (letrec ((a a))
775 a)
776 (letrec (a) (_) ((lexical a _))
777 (lexical a _)))
778
779 (pass-if-peval
780 ;; Another interesting recursive case.
781 (letrec ((a b) (b a))
782 a)
783 (letrec (a) (_) ((lexical a _))
784 (lexical a _)))
785
786 (pass-if-peval
787 ;; Another pruning case, that `a' is residualized.
788 (letrec ((a (lambda () (a)))
789 (b (lambda () (a)))
790 (c (lambda (x) x)))
791 (let ((d (foo b)))
792 (c d)))
793
794 ;; "b c a" is the current order that we get with unordered letrec,
795 ;; but it's not important to this test, so if it changes, just adapt
796 ;; the test.
797 (letrec (b c a) (_ _ _)
798 ((lambda _
799 (lambda-case
800 ((() #f #f #f () ())
801 (apply (lexical a _)))))
802 (lambda _
803 (lambda-case
804 (((x) #f #f #f () (_))
805 (lexical x _))))
806 (lambda _
807 (lambda-case
808 ((() #f #f #f () ())
809 (apply (lexical a _))))))
810 (let (d)
811 (_)
812 ((apply (toplevel foo) (lexical b _)))
813 (apply (lexical c _)
814 (lexical d _)))))
815
816 (pass-if-peval
817 ;; In this case, we can prune the bindings. `a' ends up being copied
818 ;; because it is only referenced once in the source program. Oh
819 ;; well.
820 (letrec* ((a (lambda (x) (top x)))
821 (b (lambda () a)))
822 (foo (b) (b)))
823 (apply (toplevel foo)
824 (lambda _
825 (lambda-case
826 (((x) #f #f #f () (_))
827 (apply (toplevel top) (lexical x _)))))
828 (lambda _
829 (lambda-case
830 (((x) #f #f #f () (_))
831 (apply (toplevel top) (lexical x _)))))))
832
30c3dac7
AW
833 (pass-if-peval resolve-primitives
834 ;; The inliner sees through a `let'.
835 ((let ((a 10)) (lambda (b) (* b 2))) 30)
836 (const 60))
837
838 (pass-if-peval
839 ((lambda ()
840 (define (const x) (lambda (_) x))
841 (let ((v #f))
842 ((const #t) v))))
843 (const #t))
844
564f5e70
AW
845 (pass-if-peval
846 ;; Applications of procedures with rest arguments can get inlined.
847 ((lambda (x y . z)
848 (list x y z))
849 1 2 3 4)
850 (let (z) (_) ((apply (primitive list) (const 3) (const 4)))
851 (apply (primitive list) (const 1) (const 2) (lexical z _))))
852
de1eb420
AW
853 (pass-if-peval
854 ;; Constant folding: cons of #nil does not make list
855 (cons 1 #nil)
856 (apply (primitive cons) (const 1) (const '#nil)))
857
858 (pass-if-peval
859 ;; Constant folding: cons
860 (begin (cons 1 2) #f)
861 (const #f))
862
863 (pass-if-peval
864 ;; Constant folding: cons
865 (begin (cons (foo) 2) #f)
866 (begin (apply (toplevel foo)) (const #f)))
867
868 (pass-if-peval
869 ;; Constant folding: cons
870 (if (cons 0 0) 1 2)
871 (const 1))
872
873 (pass-if-peval
874 ;; Constant folding: car+cons
875 (car (cons 1 0))
876 (const 1))
877
878 (pass-if-peval
879 ;; Constant folding: cdr+cons
880 (cdr (cons 1 0))
881 (const 0))
882
883 (pass-if-peval
884 ;; Constant folding: car+cons, impure
885 (car (cons 1 (bar)))
886 (begin (apply (toplevel bar)) (const 1)))
887
888 (pass-if-peval
889 ;; Constant folding: cdr+cons, impure
890 (cdr (cons (bar) 0))
891 (begin (apply (toplevel bar)) (const 0)))
892
893 (pass-if-peval
894 ;; Constant folding: car+list
895 (car (list 1 0))
896 (const 1))
897
898 (pass-if-peval
899 ;; Constant folding: cdr+list
900 (cdr (list 1 0))
901 (apply (primitive list) (const 0)))
902
903 (pass-if-peval
904 ;; Constant folding: car+list, impure
905 (car (list 1 (bar)))
906 (begin (apply (toplevel bar)) (const 1)))
907
908 (pass-if-peval
909 ;; Constant folding: cdr+list, impure
910 (cdr (list (bar) 0))
911 (begin (apply (toplevel bar)) (apply (primitive list) (const 0))))
912
913 (pass-if-peval
914 resolve-primitives
915 ;; Non-constant guards get lexical bindings.
916 (dynamic-wind foo (lambda () bar) baz)
917 (let (pre post) (_ _) ((toplevel foo) (toplevel baz))
918 (dynwind (lexical pre _) (toplevel bar) (lexical post _))))
919
920 (pass-if-peval
921 resolve-primitives
922 ;; Constant guards don't need lexical bindings.
923 (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
924 (dynwind
925 (lambda ()
926 (lambda-case
927 ((() #f #f #f () ()) (toplevel foo))))
928 (toplevel bar)
929 (lambda ()
930 (lambda-case
931 ((() #f #f #f () ()) (toplevel baz))))))
932
933 (pass-if-peval
934 resolve-primitives
935 ;; Prompt is removed if tag is unreferenced
936 (let ((tag (make-prompt-tag)))
937 (call-with-prompt tag
938 (lambda () 1)
939 (lambda args args)))
940 (const 1))
941
942 (pass-if-peval
943 resolve-primitives
944 ;; Prompt is removed if tag is unreferenced, with explicit stem
945 (let ((tag (make-prompt-tag "foo")))
946 (call-with-prompt tag
947 (lambda () 1)
948 (lambda args args)))
949 (const 1))
950
951 ;; Handler lambda inlined
952 (pass-if-peval
953 resolve-primitives
954 (call-with-prompt tag
955 (lambda () 1)
956 (lambda (k x) x))
957 (prompt (toplevel tag)
958 (const 1)
959 (lambda-case
960 (((k x) #f #f #f () (_ _))
961 (lexical x _)))))
962
963 ;; Handler toplevel not inlined
964 (pass-if-peval
965 resolve-primitives
966 (call-with-prompt tag
967 (lambda () 1)
968 handler)
969 (let (handler) (_) ((toplevel handler))
970 (prompt (toplevel tag)
971 (const 1)
972 (lambda-case
973 ((() #f args #f () (_))
974 (apply (primitive @apply)
975 (lexical handler _)
976 (lexical args _)))))))
977
978 (pass-if-peval
979 resolve-primitives
980 ;; `while' without `break' or `continue' has no prompts and gets its
981 ;; condition folded. Unfortunately the outer `lp' does not yet get
997ed300
AW
982 ;; elided, and the continuation tag stays around. (The continue tag
983 ;; stays around because although it is not referenced, recursively
984 ;; visiting the loop in the continue handler manages to visit the tag
985 ;; twice before aborting. The abort doesn't unroll the recursive
986 ;; reference.)
de1eb420 987 (while #t #t)
997ed300
AW
988 (let (_) (_) ((apply (primitive make-prompt-tag) . _))
989 (letrec (lp) (_)
990 ((lambda _
991 (lambda-case
992 ((() #f #f #f () ())
993 (letrec (loop) (_)
994 ((lambda _
995 (lambda-case
996 ((() #f #f #f () ())
997 (apply (lexical loop _))))))
998 (apply (lexical loop _)))))))
999 (apply (lexical lp _)))))
de1eb420
AW
1000
1001 (pass-if-peval
1002 resolve-primitives
1003 (lambda (a . rest)
1004 (apply (lambda (x y) (+ x y))
1005 a rest))
1006 (lambda _
1007 (lambda-case
1008 (((x y) #f #f #f () (_ _))
1009 _))))
1010
1011 (pass-if-peval resolve-primitives
1012 (car '(1 2))
985702f7
AW
1013 (const 1))
1014
1015 ;; If we bail out when inlining an identifier because it's too big,
1016 ;; but the identifier simply aliases some other identifier, then avoid
1017 ;; residualizing a reference to the leaf identifier. The bailout is
1018 ;; driven by the recursive-effort-limit, which is currently 100. We
1019 ;; make sure to trip it with this recursive sum thing.
1020 (pass-if-peval resolve-primitives
1021 (let ((x (let sum ((n 0) (out 0))
1022 (if (< n 10000)
1023 (sum (1+ n) (+ out n))
1024 out))))
1025 ((lambda (y) (list y)) x))
1026 (let (x) (_) (_)
f49fd9af
AW
1027 (apply (primitive list) (lexical x _))))
1028
1029 ;; Here we test that a common test in a chain of ifs gets lifted.
1030 (pass-if-peval resolve-primitives
1031 (if (and (struct? x) (eq? (struct-vtable x) A))
1032 (foo x)
1033 (if (and (struct? x) (eq? (struct-vtable x) B))
1034 (bar x)
1035 (if (and (struct? x) (eq? (struct-vtable x) C))
1036 (baz x)
1037 (qux x))))
1038 (let (failure) (_) ((lambda _
1039 (lambda-case
1040 ((() #f #f #f () ())
1041 (apply (toplevel qux) (toplevel x))))))
1042 (if (apply (primitive struct?) (toplevel x))
1043 (if (apply (primitive eq?)
1044 (apply (primitive struct-vtable) (toplevel x))
1045 (toplevel A))
1046 (apply (toplevel foo) (toplevel x))
1047 (if (apply (primitive eq?)
1048 (apply (primitive struct-vtable) (toplevel x))
1049 (toplevel B))
1050 (apply (toplevel bar) (toplevel x))
1051 (if (apply (primitive eq?)
1052 (apply (primitive struct-vtable) (toplevel x))
1053 (toplevel C))
1054 (apply (toplevel baz) (toplevel x))
1055 (apply (lexical failure _)))))
9b1750ed
AW
1056 (apply (lexical failure _)))))
1057
1058 ;; Multiple common tests should get lifted as well.
1059 (pass-if-peval resolve-primitives
1060 (if (and (struct? x) (eq? (struct-vtable x) A) B)
1061 (foo x)
1062 (if (and (struct? x) (eq? (struct-vtable x) A) C)
1063 (bar x)
1064 (if (and (struct? x) (eq? (struct-vtable x) A) D)
1065 (baz x)
1066 (qux x))))
1067 (let (failure) (_) ((lambda _
1068 (lambda-case
1069 ((() #f #f #f () ())
1070 (apply (toplevel qux) (toplevel x))))))
1071 (if (apply (primitive struct?) (toplevel x))
1072 (if (apply (primitive eq?)
1073 (apply (primitive struct-vtable) (toplevel x))
1074 (toplevel A))
1075 (if (toplevel B)
1076 (apply (toplevel foo) (toplevel x))
1077 (if (toplevel C)
1078 (apply (toplevel bar) (toplevel x))
1079 (if (toplevel D)
1080 (apply (toplevel baz) (toplevel x))
1081 (apply (lexical failure _)))))
1082 (apply (lexical failure _)))
3d2bcd2c
AW
1083 (apply (lexical failure _)))))
1084
1085 (pass-if-peval resolve-primitives
1086 (apply (lambda (x y) (cons x y)) '(1 2))
1087 (apply (primitive cons) (const 1) (const 2)))
1088
1089 (pass-if-peval resolve-primitives
1090 (apply (lambda (x y) (cons x y)) (list 1 2))
997ed300
AW
1091 (apply (primitive cons) (const 1) (const 2)))
1092
1093 (pass-if-peval resolve-primitives
1094 (let ((t (make-prompt-tag)))
1095 (call-with-prompt t
1096 (lambda () (abort-to-prompt t 1 2 3))
1097 (lambda (k x y z) (list x y z))))
1098 (apply (primitive 'list) (const 1) (const 2) (const 3))))