Merge branch 'stable-2.0'
[bpt/guile.git] / test-suite / tests / peval.test
1 ;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
2 ;;;; Andy Wingo <wingo@pobox.com> --- May 2009
3 ;;;;
4 ;;;; Copyright (C) 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
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 (rnrs bytevectors) ;; for the bytevector primitives
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 ()
36 ((_ in pat)
37 (pass-if-peval in pat
38 (expand-primitives
39 (resolve-primitives
40 (compile 'in #:from 'scheme #:to 'tree-il)
41 (current-module)))))
42 ((_ in pat code)
43 (pass-if 'in
44 (let ((evaled (unparse-tree-il (peval code))))
45 (pmatch evaled
46 (pat #t)
47 (_ (pk 'peval-mismatch)
48 ((@ (ice-9 pretty-print) pretty-print)
49 'in)
50 (newline)
51 ((@ (ice-9 pretty-print) pretty-print)
52 evaled)
53 (newline)
54 ((@ (ice-9 pretty-print) pretty-print)
55 'pat)
56 (newline)
57 #f)))))))
58
59 \f
60 (with-test-prefix "partial evaluation"
61
62 (pass-if-peval
63 ;; First order, primitive.
64 (let ((x 1) (y 2)) (+ x y))
65 (const 3))
66
67 (pass-if-peval
68 ;; First order, thunk.
69 (let ((x 1) (y 2))
70 (let ((f (lambda () (+ x y))))
71 (f)))
72 (const 3))
73
74 (pass-if-peval
75 ;; First order, let-values (requires primitive expansion for
76 ;; `call-with-values'.)
77 (let ((x 0))
78 (call-with-values
79 (lambda () (if (zero? x) (values 1 2) (values 3 4)))
80 (lambda (a b)
81 (+ a b))))
82 (const 3))
83
84 (pass-if-peval
85 ;; First order, multiple values.
86 (let ((x 1) (y 2))
87 (values x y))
88 (primcall values (const 1) (const 2)))
89
90 (pass-if-peval
91 ;; First order, multiple values truncated.
92 (let ((x (values 1 'a)) (y 2))
93 (values x y))
94 (primcall values (const 1) (const 2)))
95
96 (pass-if-peval
97 ;; First order, multiple values truncated.
98 (or (values 1 2) 3)
99 (const 1))
100
101 (pass-if-peval
102 ;; First order, coalesced, mutability preserved.
103 (cons 0 (cons 1 (cons 2 (list 3 4 5))))
104 (primcall list
105 (const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
106
107 (pass-if-peval
108 ;; First order, coalesced, immutability preserved.
109 (cons 0 (cons 1 (cons 2 '(3 4 5))))
110 (primcall cons (const 0)
111 (primcall cons (const 1)
112 (primcall cons (const 2)
113 (const (3 4 5))))))
114
115 ;; These two tests doesn't work any more because we changed the way we
116 ;; deal with constants -- now the algorithm will see a construction as
117 ;; being bound to the lexical, so it won't propagate it. It can't
118 ;; even propagate it in the case that it is only referenced once,
119 ;; because:
120 ;;
121 ;; (let ((x (cons 1 2))) (lambda () x))
122 ;;
123 ;; is not the same as
124 ;;
125 ;; (lambda () (cons 1 2))
126 ;;
127 ;; Perhaps if we determined that not only was it only referenced once,
128 ;; it was not closed over by a lambda, then we could propagate it, and
129 ;; re-enable these two tests.
130 ;;
131 #;
132 (pass-if-peval
133 ;; First order, mutability preserved.
134 (let loop ((i 3) (r '()))
135 (if (zero? i)
136 r
137 (loop (1- i) (cons (cons i i) r))))
138 (primcall list
139 (primcall cons (const 1) (const 1))
140 (primcall cons (const 2) (const 2))
141 (primcall cons (const 3) (const 3))))
142 ;;
143 ;; See above.
144 #;
145 (pass-if-peval
146 ;; First order, evaluated.
147 (let loop ((i 7)
148 (r '()))
149 (if (<= i 0)
150 (car r)
151 (loop (1- i) (cons i r))))
152 (const 1))
153
154 ;; Instead here are tests for what happens for the above cases: they
155 ;; unroll but they don't fold.
156 (pass-if-peval
157 (let loop ((i 3) (r '()))
158 (if (zero? i)
159 r
160 (loop (1- i) (cons (cons i i) r))))
161 (let (r) (_)
162 ((primcall list
163 (primcall cons (const 3) (const 3))))
164 (let (r) (_)
165 ((primcall cons
166 (primcall cons (const 2) (const 2))
167 (lexical r _)))
168 (primcall cons
169 (primcall cons (const 1) (const 1))
170 (lexical r _)))))
171
172 ;; See above.
173 (pass-if-peval
174 (let loop ((i 4)
175 (r '()))
176 (if (<= i 0)
177 (car r)
178 (loop (1- i) (cons i r))))
179 (let (r) (_)
180 ((primcall list (const 4)))
181 (let (r) (_)
182 ((primcall cons
183 (const 3)
184 (lexical r _)))
185 (let (r) (_)
186 ((primcall cons
187 (const 2)
188 (lexical r _)))
189 (let (r) (_)
190 ((primcall cons
191 (const 1)
192 (lexical r _)))
193 (primcall car
194 (lexical r _)))))))
195
196 ;; Static sums.
197 (pass-if-peval
198 (let loop ((l '(1 2 3 4)) (sum 0))
199 (if (null? l)
200 sum
201 (loop (cdr l) (+ sum (car l)))))
202 (const 10))
203
204 (pass-if-peval
205 (let ((string->chars
206 (lambda (s)
207 (define (char-at n)
208 (string-ref s n))
209 (define (len)
210 (string-length s))
211 (let loop ((i 0))
212 (if (< i (len))
213 (cons (char-at i)
214 (loop (1+ i)))
215 '())))))
216 (string->chars "yo"))
217 (primcall list (const #\y) (const #\o)))
218
219 (pass-if-peval
220 ;; Primitives in module-refs are resolved (the expansion of `pmatch'
221 ;; below leads to calls to (@@ (system base pmatch) car) and
222 ;; similar, which is what we want to be inlined.)
223 (begin
224 (use-modules (system base pmatch))
225 (pmatch '(a b c d)
226 ((a b . _)
227 #t)))
228 (seq (call . _)
229 (const #t)))
230
231 (pass-if-peval
232 ;; Mutability preserved.
233 ((lambda (x y z) (list x y z)) 1 2 3)
234 (primcall list (const 1) (const 2) (const 3)))
235
236 (pass-if-peval
237 ;; Don't propagate effect-free expressions that operate on mutable
238 ;; objects.
239 (let* ((x (list 1))
240 (y (car x)))
241 (set-car! x 0)
242 y)
243 (let (x) (_) ((primcall list (const 1)))
244 (let (y) (_) ((primcall car (lexical x _)))
245 (seq
246 (primcall set-car! (lexical x _) (const 0))
247 (lexical y _)))))
248
249 (pass-if-peval
250 ;; Don't propagate effect-free expressions that operate on objects we
251 ;; don't know about.
252 (let ((y (car x)))
253 (set-car! x 0)
254 y)
255 (let (y) (_) ((primcall car (toplevel x)))
256 (seq
257 (primcall set-car! (toplevel x) (const 0))
258 (lexical y _))))
259
260 (pass-if-peval
261 ;; Infinite recursion
262 ((lambda (x) (x x)) (lambda (x) (x x)))
263 (let (x) (_)
264 ((lambda _
265 (lambda-case
266 (((x) _ _ _ _ _)
267 (call (lexical x _) (lexical x _))))))
268 (call (lexical x _) (lexical x _))))
269
270 (pass-if-peval
271 ;; First order, aliased primitive.
272 (let* ((x *) (y (x 1 2))) y)
273 (const 2))
274
275 (pass-if-peval
276 ;; First order, shadowed primitive.
277 (begin
278 (define (+ x y) (pk x y))
279 (+ 1 2))
280 (seq
281 (define +
282 (lambda (_)
283 (lambda-case
284 (((x y) #f #f #f () (_ _))
285 (call (toplevel pk) (lexical x _) (lexical y _))))))
286 (call (toplevel +) (const 1) (const 2))))
287
288 (pass-if-peval
289 ;; First-order, effects preserved.
290 (let ((x 2))
291 (do-something!)
292 x)
293 (seq
294 (call (toplevel do-something!))
295 (const 2)))
296
297 (pass-if-peval
298 ;; First order, residual bindings removed.
299 (let ((x 2) (y 3))
300 (* (+ x y) z))
301 (primcall * (const 5) (toplevel z)))
302
303 (pass-if-peval
304 ;; First order, with lambda.
305 (define (foo x)
306 (define (bar z) (* z z))
307 (+ x (bar 3)))
308 (define foo
309 (lambda (_)
310 (lambda-case
311 (((x) #f #f #f () (_))
312 (primcall + (lexical x _) (const 9)))))))
313
314 (pass-if-peval
315 ;; First order, with lambda inlined & specialized twice.
316 (let ((f (lambda (x y)
317 (+ (* x top) y)))
318 (x 2)
319 (y 3))
320 (+ (* x (f x y))
321 (f something x)))
322 (primcall +
323 (primcall *
324 (const 2)
325 (primcall + ; (f 2 3)
326 (primcall *
327 (const 2)
328 (toplevel top))
329 (const 3)))
330 (let (x) (_) ((toplevel something)) ; (f something 2)
331 ;; `something' is not const, so preserve order of
332 ;; effects with a lexical binding.
333 (primcall +
334 (primcall *
335 (lexical x _)
336 (toplevel top))
337 (const 2)))))
338
339 (pass-if-peval
340 ;; First order, with lambda inlined & specialized 3 times.
341 (let ((f (lambda (x y) (if (> x 0) y x))))
342 (+ (f -1 0)
343 (f 1 0)
344 (f -1 y)
345 (f 2 y)
346 (f z y)))
347 (primcall
348 +
349 (primcall
350 +
351 (primcall
352 +
353 (const -1) ; (f -1 0)
354 (seq (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 (primcall > (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 (call (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 (seq
391 (call (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) (_) ((call (toplevel foo)))
431 (primcall + (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 (primcall 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 (call (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 (primcall 1- (lexical x2 _))))))))
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 (expand-primitives
488 (resolve-primitives
489 (compile
490 '(let ((make-adder
491 (lambda (x) (lambda (y) (+ x y)))))
492 (cons (make-adder 1) (make-adder 2)))
493 #:to 'tree-il)
494 (current-module)))))
495 ((primcall cons
496 (lambda ()
497 (lambda-case
498 (((y) #f #f #f () (,gensym1))
499 (primcall +
500 (const 1)
501 (lexical y ,ref1)))))
502 (lambda ()
503 (lambda-case
504 (((y) #f #f #f () (,gensym2))
505 (primcall +
506 (const 2)
507 (lexical y ,ref2))))))
508 (and (eq? gensym1 ref1)
509 (eq? gensym2 ref2)
510 (not (eq? gensym1 gensym2))))
511 (_ #f)))
512
513 (pass-if-peval
514 ;; Unused letrec bindings are pruned.
515 (letrec ((a (lambda () (b)))
516 (b (lambda () (a)))
517 (c (lambda (x) x)))
518 (c 10))
519 (const 10))
520
521 (pass-if-peval
522 ;; Unused letrec bindings are pruned.
523 (letrec ((a (foo!))
524 (b (lambda () (a)))
525 (c (lambda (x) x)))
526 (c 10))
527 (seq (call (toplevel foo!))
528 (const 10)))
529
530 (pass-if-peval
531 ;; Higher order, mutually recursive procedures.
532 (letrec ((even? (lambda (x)
533 (or (= 0 x)
534 (odd? (- x 1)))))
535 (odd? (lambda (x)
536 (not (even? x)))))
537 (and (even? 4) (odd? 7)))
538 (const #t))
539
540 (pass-if-peval
541 ;; Memv with constants.
542 (memv 1 '(3 2 1))
543 (const '(1)))
544
545 (pass-if-peval
546 ;; Memv with non-constant list. It could fold but doesn't
547 ;; currently.
548 (memv 1 (list 3 2 1))
549 (primcall memv
550 (const 1)
551 (primcall list (const 3) (const 2) (const 1))))
552
553 (pass-if-peval
554 ;; Memv with non-constant key, constant list, test context
555 (case foo
556 ((3 2 1) 'a)
557 (else 'b))
558 (let (key) (_) ((toplevel foo))
559 (if (if (primcall eqv? (lexical key _) (const 3))
560 (const #t)
561 (if (primcall eqv? (lexical key _) (const 2))
562 (const #t)
563 (primcall eqv? (lexical key _) (const 1))))
564 (const a)
565 (const b))))
566
567 (pass-if-peval
568 ;; Memv with non-constant key, empty list, test context.
569 (case foo
570 (() 'a)
571 (else 'b))
572 (seq (toplevel foo) (const 'b)))
573
574 ;;
575 ;; Below are cases where constant propagation should bail out.
576 ;;
577
578 (pass-if-peval
579 ;; Non-constant lexical is not propagated.
580 (let ((v (make-vector 6 #f)))
581 (lambda (n)
582 (vector-set! v n n)))
583 (let (v) (_)
584 ((primcall make-vector (const 6) (const #f)))
585 (lambda ()
586 (lambda-case
587 (((n) #f #f #f () (_))
588 (primcall vector-set!
589 (lexical v _) (lexical n _) (lexical n _)))))))
590
591 (pass-if-peval
592 ;; Mutable lexical is not propagated.
593 (let ((v (vector 1 2 3)))
594 (lambda ()
595 v))
596 (let (v) (_)
597 ((primcall vector (const 1) (const 2) (const 3)))
598 (lambda ()
599 (lambda-case
600 ((() #f #f #f () ())
601 (lexical v _))))))
602
603 (pass-if-peval
604 ;; Lexical that is not provably pure is not inlined nor propagated.
605 (let* ((x (if (> p q) (frob!) (display 'chbouib)))
606 (y (* x 2)))
607 (+ x x y))
608 (let (x) (_) ((if (primcall > (toplevel p) (toplevel q))
609 (call (toplevel frob!))
610 (call (toplevel display) (const chbouib))))
611 (let (y) (_) ((primcall * (lexical x _) (const 2)))
612 (primcall +
613 (primcall + (lexical x _) (lexical x _))
614 (lexical y _)))))
615
616 (pass-if-peval
617 ;; Non-constant arguments not propagated to lambdas.
618 ((lambda (x y z)
619 (vector-set! x 0 0)
620 (set-car! y 0)
621 (set-cdr! z '()))
622 (vector 1 2 3)
623 (make-list 10)
624 (list 1 2 3))
625 (let (x y z) (_ _ _)
626 ((primcall vector (const 1) (const 2) (const 3))
627 (call (toplevel make-list) (const 10))
628 (primcall list (const 1) (const 2) (const 3)))
629 (seq
630 (primcall vector-set!
631 (lexical x _) (const 0) (const 0))
632 (seq (primcall set-car!
633 (lexical y _) (const 0))
634 (primcall set-cdr!
635 (lexical z _) (const ()))))))
636
637 (pass-if-peval
638 (let ((foo top-foo) (bar top-bar))
639 (let* ((g (lambda (x y) (+ x y)))
640 (f (lambda (g x) (g x x))))
641 (+ (f g foo) (f g bar))))
642 (let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
643 (primcall +
644 (primcall + (lexical foo _) (lexical foo _))
645 (primcall + (lexical bar _) (lexical bar _)))))
646
647 (pass-if-peval
648 ;; Fresh objects are not turned into constants, nor are constants
649 ;; turned into fresh objects.
650 (let* ((c '(2 3))
651 (x (cons 1 c))
652 (y (cons 0 x)))
653 y)
654 (let (x) (_) ((primcall cons (const 1) (const (2 3))))
655 (primcall cons (const 0) (lexical x _))))
656
657 (pass-if-peval
658 ;; Bindings mutated.
659 (let ((x 2))
660 (set! x 3)
661 x)
662 (let (x) (_) ((const 2))
663 (seq
664 (set! (lexical x _) (const 3))
665 (lexical x _))))
666
667 (pass-if-peval
668 ;; Bindings mutated.
669 (letrec ((x 0)
670 (f (lambda ()
671 (set! x (+ 1 x))
672 x)))
673 (frob f) ; may mutate `x'
674 x)
675 (letrec (x) (_) ((const 0))
676 (seq
677 (call (toplevel frob) (lambda _ _))
678 (lexical x _))))
679
680 (pass-if-peval
681 ;; Bindings mutated.
682 (letrec ((f (lambda (x)
683 (set! f (lambda (_) x))
684 x)))
685 (f 2))
686 (letrec _ . _))
687
688 (pass-if-peval
689 ;; Bindings possibly mutated.
690 (let ((x (make-foo)))
691 (frob! x) ; may mutate `x'
692 x)
693 (let (x) (_) ((call (toplevel make-foo)))
694 (seq
695 (call (toplevel frob!) (lexical x _))
696 (lexical x _))))
697
698 (pass-if-peval
699 ;; Inlining stops at recursive calls with dynamic arguments.
700 (let loop ((x x))
701 (if (< x 0) x (loop (1- x))))
702 (letrec (loop) (_) ((lambda (_)
703 (lambda-case
704 (((x) #f #f #f () (_))
705 (if _ _
706 (call (lexical loop _)
707 (primcall 1-
708 (lexical x _))))))))
709 (call (lexical loop _) (toplevel x))))
710
711 (pass-if-peval
712 ;; Recursion on the 2nd argument is fully evaluated.
713 (let ((x (top)))
714 (let loop ((x x) (y 10))
715 (if (> y 0)
716 (loop x (1- y))
717 (foo x y))))
718 (let (x) (_) ((call (toplevel top)))
719 (call (toplevel foo) (lexical x _) (const 0))))
720
721 (pass-if-peval
722 ;; Inlining aborted when residual code contains recursive calls.
723 ;;
724 ;; <http://debbugs.gnu.org/9542>
725 (let loop ((x x) (y 0))
726 (if (> y 0)
727 (loop (1- x) (1- y))
728 (if (< x 0)
729 x
730 (loop (1+ x) (1+ y)))))
731 (letrec (loop) (_) ((lambda (_)
732 (lambda-case
733 (((x y) #f #f #f () (_ _))
734 (if (primcall >
735 (lexical y _) (const 0))
736 _ _)))))
737 (call (lexical loop _) (toplevel x) (const 0))))
738
739 (pass-if-peval
740 ;; Infinite recursion: `peval' gives up and leaves it as is.
741 (letrec ((f (lambda (x) (g (1- x))))
742 (g (lambda (x) (h (1+ x))))
743 (h (lambda (x) (f x))))
744 (f 0))
745 (letrec _ . _))
746
747 (pass-if-peval
748 ;; Infinite recursion: all the arguments to `loop' are static, but
749 ;; unrolling it would lead `peval' to enter an infinite loop.
750 (let loop ((x 0))
751 (and (< x top)
752 (loop (1+ x))))
753 (letrec (loop) (_) ((lambda . _))
754 (call (lexical loop _) (const 0))))
755
756 (pass-if-peval
757 ;; This test checks that the `start' binding is indeed residualized.
758 ;; See the `referenced?' procedure in peval's `prune-bindings'.
759 (let ((pos 0))
760 (let ((here (let ((start pos)) (lambda () start))))
761 (set! pos 1) ;; Cause references to `pos' to residualize.
762 (here)))
763 (let (pos) (_) ((const 0))
764 (let (here) (_) (_)
765 (seq
766 (set! (lexical pos _) (const 1))
767 (call (lexical here _))))))
768
769 (pass-if-peval
770 ;; FIXME: should this one residualize the binding?
771 (letrec ((a a))
772 1)
773 (const 1))
774
775 (pass-if-peval
776 ;; This is a fun one for peval to handle.
777 (letrec ((a a))
778 a)
779 (letrec (a) (_) ((lexical a _))
780 (lexical a _)))
781
782 (pass-if-peval
783 ;; Another interesting recursive case.
784 (letrec ((a b) (b a))
785 a)
786 (letrec (a) (_) ((lexical a _))
787 (lexical a _)))
788
789 (pass-if-peval
790 ;; Another pruning case, that `a' is residualized.
791 (letrec ((a (lambda () (a)))
792 (b (lambda () (a)))
793 (c (lambda (x) x)))
794 (let ((d (foo b)))
795 (c d)))
796
797 ;; "b c a" is the current order that we get with unordered letrec,
798 ;; but it's not important to this test, so if it changes, just adapt
799 ;; the test.
800 (letrec (b c a) (_ _ _)
801 ((lambda _
802 (lambda-case
803 ((() #f #f #f () ())
804 (call (lexical a _)))))
805 (lambda _
806 (lambda-case
807 (((x) #f #f #f () (_))
808 (lexical x _))))
809 (lambda _
810 (lambda-case
811 ((() #f #f #f () ())
812 (call (lexical a _))))))
813 (let (d)
814 (_)
815 ((call (toplevel foo) (lexical b _)))
816 (call (lexical c _) (lexical d _)))))
817
818 (pass-if-peval
819 ;; In this case, we can prune the bindings. `a' ends up being copied
820 ;; because it is only referenced once in the source program. Oh
821 ;; well.
822 (letrec* ((a (lambda (x) (top x)))
823 (b (lambda () a)))
824 (foo (b) (b)))
825 (call (toplevel foo)
826 (lambda _
827 (lambda-case
828 (((x) #f #f #f () (_))
829 (call (toplevel top) (lexical x _)))))
830 (lambda _
831 (lambda-case
832 (((x) #f #f #f () (_))
833 (call (toplevel top) (lexical x _)))))))
834
835 (pass-if-peval
836 ;; The inliner sees through a `let'.
837 ((let ((a 10)) (lambda (b) (* b 2))) 30)
838 (const 60))
839
840 (pass-if-peval
841 ((lambda ()
842 (define (const x) (lambda (_) x))
843 (let ((v #f))
844 ((const #t) v))))
845 (const #t))
846
847 (pass-if-peval
848 ;; Applications of procedures with rest arguments can get inlined.
849 ((lambda (x y . z)
850 (list x y z))
851 1 2 3 4)
852 (let (z) (_) ((primcall list (const 3) (const 4)))
853 (primcall list (const 1) (const 2) (lexical z _))))
854
855 (pass-if-peval
856 ;; Unmutated lists can get inlined.
857 (let ((args (list 2 3)))
858 (apply (lambda (x y z w)
859 (list x y z w))
860 0 1 args))
861 (primcall list (const 0) (const 1) (const 2) (const 3)))
862
863 (pass-if-peval
864 ;; However if the list might have been mutated, it doesn't propagate.
865 (let ((args (list 2 3)))
866 (foo! args)
867 (apply (lambda (x y z w)
868 (list x y z w))
869 0 1 args))
870 (let (args) (_) ((primcall list (const 2) (const 3)))
871 (seq
872 (call (toplevel foo!) (lexical args _))
873 (primcall apply
874 (lambda ()
875 (lambda-case
876 (((x y z w) #f #f #f () (_ _ _ _))
877 (primcall list
878 (lexical x _) (lexical y _)
879 (lexical z _) (lexical w _)))))
880 (const 0)
881 (const 1)
882 (lexical args _)))))
883
884 (pass-if-peval
885 ;; Here the `args' that gets built by the application of the lambda
886 ;; takes more than effort "10" to visit. Test that we fall back to
887 ;; the source expression of the operand, which is still a call to
888 ;; `list', so the inlining still happens.
889 (lambda (bv offset n)
890 (let ((x (bytevector-ieee-single-native-ref
891 bv
892 (+ offset 0)))
893 (y (bytevector-ieee-single-native-ref
894 bv
895 (+ offset 4))))
896 (let ((args (list x y)))
897 (apply
898 (lambda (bv offset x y)
899 (bytevector-ieee-single-native-set!
900 bv
901 (+ offset 0)
902 x)
903 (bytevector-ieee-single-native-set!
904 bv
905 (+ offset 4)
906 y))
907 bv
908 offset
909 args))))
910 (lambda ()
911 (lambda-case
912 (((bv offset n) #f #f #f () (_ _ _))
913 (let (x y) (_ _) ((primcall bytevector-ieee-single-native-ref
914 (lexical bv _)
915 (primcall +
916 (lexical offset _) (const 0)))
917 (primcall bytevector-ieee-single-native-ref
918 (lexical bv _)
919 (primcall +
920 (lexical offset _) (const 4))))
921 (seq
922 (primcall bytevector-ieee-single-native-set!
923 (lexical bv _)
924 (primcall +
925 (lexical offset _) (const 0))
926 (lexical x _))
927 (primcall bytevector-ieee-single-native-set!
928 (lexical bv _)
929 (primcall +
930 (lexical offset _) (const 4))
931 (lexical y _))))))))
932
933 (pass-if-peval
934 ;; Here we ensure that non-constant expressions are not copied.
935 (lambda ()
936 (let ((args (list (foo!))))
937 (apply
938 (lambda (z x)
939 (list z x))
940 ;; This toplevel ref might raise an unbound variable exception.
941 ;; The effects of `(foo!)' must be visible before this effect.
942 z
943 args)))
944 (lambda ()
945 (lambda-case
946 ((() #f #f #f () ())
947 (let (_) (_) ((call (toplevel foo!)))
948 (let (z) (_) ((toplevel z))
949 (primcall 'list
950 (lexical z _)
951 (lexical _ _))))))))
952
953 (pass-if-peval
954 ;; Rest args referenced more than once are not destructured.
955 (lambda ()
956 (let ((args (list 'foo)))
957 (set-car! args 'bar)
958 (apply
959 (lambda (z x)
960 (list z x))
961 z
962 args)))
963 (lambda ()
964 (lambda-case
965 ((() #f #f #f () ())
966 (let (args) (_)
967 ((primcall list (const foo)))
968 (seq
969 (primcall set-car! (lexical args _) (const bar))
970 (primcall apply
971 (lambda . _)
972 (toplevel z)
973 (lexical args _))))))))
974
975 (pass-if-peval
976 ;; Let-values inlining, even with consumers with rest args.
977 (call-with-values (lambda () (values 1 2))
978 (lambda args
979 (apply list args)))
980 (primcall list (const 1) (const 2)))
981
982 (pass-if-peval
983 ;; When we can't inline let-values but can prove that the producer
984 ;; has just one value, reduce to "let" (which can then fold
985 ;; further).
986 (call-with-values (lambda () (if foo 1 2))
987 (lambda args
988 (apply values args)))
989 (if (toplevel foo) (const 1) (const 2)))
990
991 (pass-if-peval
992 ;; Constant folding: cons of #nil does not make list
993 (cons 1 #nil)
994 (primcall cons (const 1) (const '#nil)))
995
996 (pass-if-peval
997 ;; Constant folding: cons
998 (begin (cons 1 2) #f)
999 (const #f))
1000
1001 (pass-if-peval
1002 ;; Constant folding: cons
1003 (begin (cons (foo) 2) #f)
1004 (seq (call (toplevel foo)) (const #f)))
1005
1006 (pass-if-peval
1007 ;; Constant folding: cons
1008 (if (cons 0 0) 1 2)
1009 (const 1))
1010
1011 (pass-if-peval
1012 ;; Constant folding: car+cons
1013 (car (cons 1 0))
1014 (const 1))
1015
1016 (pass-if-peval
1017 ;; Constant folding: cdr+cons
1018 (cdr (cons 1 0))
1019 (const 0))
1020
1021 (pass-if-peval
1022 ;; Constant folding: car+cons, impure
1023 (car (cons 1 (bar)))
1024 (seq (call (toplevel bar)) (const 1)))
1025
1026 (pass-if-peval
1027 ;; Constant folding: cdr+cons, impure
1028 (cdr (cons (bar) 0))
1029 (seq (call (toplevel bar)) (const 0)))
1030
1031 (pass-if-peval
1032 ;; Constant folding: car+list
1033 (car (list 1 0))
1034 (const 1))
1035
1036 (pass-if-peval
1037 ;; Constant folding: cdr+list
1038 (cdr (list 1 0))
1039 (primcall list (const 0)))
1040
1041 (pass-if-peval
1042 ;; Constant folding: car+list, impure
1043 (car (list 1 (bar)))
1044 (seq (call (toplevel bar)) (const 1)))
1045
1046 (pass-if-peval
1047 ;; Constant folding: cdr+list, impure
1048 (cdr (list (bar) 0))
1049 (seq (call (toplevel bar)) (primcall list (const 0))))
1050
1051 (pass-if-peval
1052 ;; Equality primitive: same lexical
1053 (let ((x (random))) (eq? x x))
1054 (seq (call (toplevel random)) (const #t)))
1055
1056 (pass-if-peval
1057 ;; Equality primitive: merge lexical identities
1058 (let* ((x (random)) (y x)) (eq? x y))
1059 (seq (call (toplevel random)) (const #t)))
1060
1061 (pass-if-peval
1062 ;; Non-constant guards get lexical bindings, invocation of winder and
1063 ;; unwinder lifted out. Unfortunately both have the generic variable
1064 ;; name "tmp", so we can't distinguish them in this test, and they
1065 ;; also collide in generic names with the single-value result from
1066 ;; the dynwind; alack.
1067 (dynamic-wind foo (lambda () bar) baz)
1068 (let (tmp tmp) (_ _) ((toplevel foo) (toplevel baz))
1069 (seq (seq (if (primcall thunk? (lexical tmp _))
1070 (call (lexical tmp _))
1071 (primcall scm-error . _))
1072 (primcall wind (lexical tmp _) (lexical tmp _)))
1073 (let (tmp) (_) ((toplevel bar))
1074 (seq (seq (primcall unwind)
1075 (call (lexical tmp _)))
1076 (lexical tmp _))))))
1077
1078 (pass-if-peval
1079 ;; Constant guards don't need lexical bindings or thunk? checks.
1080 (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
1081 (seq (seq (toplevel foo)
1082 (primcall wind
1083 (lambda ()
1084 (lambda-case
1085 ((() #f #f #f () ()) (toplevel foo))))
1086 (lambda ()
1087 (lambda-case
1088 ((() #f #f #f () ()) (toplevel baz))))))
1089 (let (tmp) (_) ((toplevel bar))
1090 (seq (seq (primcall unwind)
1091 (toplevel baz))
1092 (lexical tmp _)))))
1093
1094 (pass-if-peval
1095 ;; Dynwind bodies that return an unknown number of values need a
1096 ;; let-values.
1097 (dynamic-wind (lambda () foo) (lambda () (bar)) (lambda () baz))
1098 (seq (seq (toplevel foo)
1099 (primcall wind
1100 (lambda ()
1101 (lambda-case
1102 ((() #f #f #f () ()) (toplevel foo))))
1103 (lambda ()
1104 (lambda-case
1105 ((() #f #f #f () ()) (toplevel baz))))))
1106 (let-values (call (toplevel bar))
1107 (lambda-case
1108 ((() #f vals #f () (_))
1109 (seq (seq (primcall unwind)
1110 (toplevel baz))
1111 (primcall apply (primitive values) (lexical vals _))))))))
1112
1113 (pass-if-peval
1114 ;; Prompt is removed if tag is unreferenced
1115 (let ((tag (make-prompt-tag)))
1116 (call-with-prompt tag
1117 (lambda () 1)
1118 (lambda args args)))
1119 (const 1))
1120
1121 (pass-if-peval
1122 ;; Prompt is removed if tag is unreferenced, with explicit stem
1123 (let ((tag (make-prompt-tag "foo")))
1124 (call-with-prompt tag
1125 (lambda () 1)
1126 (lambda args args)))
1127 (const 1))
1128
1129 ;; Handler lambda inlined
1130 (pass-if-peval
1131 (call-with-prompt tag
1132 (lambda () 1)
1133 (lambda (k x) x))
1134 (prompt #t
1135 (toplevel tag)
1136 (const 1)
1137 (lambda _
1138 (lambda-case
1139 (((k x) #f #f #f () (_ _))
1140 (lexical x _))))))
1141
1142 ;; Handler toplevel not inlined
1143 (pass-if-peval
1144 (call-with-prompt tag
1145 (lambda () 1)
1146 handler)
1147 (prompt #f
1148 (toplevel tag)
1149 (lambda _
1150 (lambda-case
1151 ((() #f #f #f () ())
1152 (const 1))))
1153 (toplevel handler)))
1154
1155 (pass-if-peval
1156 ;; `while' without `break' or `continue' has no prompts and gets its
1157 ;; condition folded. Unfortunately the outer `lp' does not yet get
1158 ;; elided, and the continuation tag stays around. (The continue tag
1159 ;; stays around because although it is not referenced, recursively
1160 ;; visiting the loop in the continue handler manages to visit the tag
1161 ;; twice before aborting. The abort doesn't unroll the recursive
1162 ;; reference.)
1163 (while #t #t)
1164 (let (_) (_) ((primcall make-prompt-tag . _))
1165 (letrec (lp) (_)
1166 ((lambda _
1167 (lambda-case
1168 ((() #f #f #f () ())
1169 (letrec (loop) (_)
1170 ((lambda _
1171 (lambda-case
1172 ((() #f #f #f () ())
1173 (call (lexical loop _))))))
1174 (call (lexical loop _)))))))
1175 (call (lexical lp _)))))
1176
1177 (pass-if-peval
1178 (lambda (a . rest)
1179 (apply (lambda (x y) (+ x y))
1180 a rest))
1181 (lambda _
1182 (lambda-case
1183 (((x y) #f #f #f () (_ _))
1184 _))))
1185
1186 (pass-if-peval
1187 (car '(1 2))
1188 (const 1))
1189
1190 ;; If we bail out when inlining an identifier because it's too big,
1191 ;; but the identifier simply aliases some other identifier, then avoid
1192 ;; residualizing a reference to the leaf identifier. The bailout is
1193 ;; driven by the recursive-effort-limit, which is currently 100. We
1194 ;; make sure to trip it with this recursive sum thing.
1195 (pass-if-peval
1196 (let ((x (let sum ((n 0) (out 0))
1197 (if (< n 10000)
1198 (sum (1+ n) (+ out n))
1199 out))))
1200 ((lambda (y) (list y)) x))
1201 (let (x) (_) (_)
1202 (primcall list (lexical x _))))
1203
1204 ;; Here we test that a common test in a chain of ifs gets lifted.
1205 (pass-if-peval
1206 (if (and (struct? x) (eq? (struct-vtable x) A))
1207 (foo x)
1208 (if (and (struct? x) (eq? (struct-vtable x) B))
1209 (bar x)
1210 (if (and (struct? x) (eq? (struct-vtable x) C))
1211 (baz x)
1212 (qux x))))
1213 (let (failure) (_) ((lambda _
1214 (lambda-case
1215 ((() #f #f #f () ())
1216 (call (toplevel qux) (toplevel x))))))
1217 (if (primcall struct? (toplevel x))
1218 (if (primcall eq?
1219 (primcall struct-vtable (toplevel x))
1220 (toplevel A))
1221 (call (toplevel foo) (toplevel x))
1222 (if (primcall eq?
1223 (primcall struct-vtable (toplevel x))
1224 (toplevel B))
1225 (call (toplevel bar) (toplevel x))
1226 (if (primcall eq?
1227 (primcall struct-vtable (toplevel x))
1228 (toplevel C))
1229 (call (toplevel baz) (toplevel x))
1230 (call (lexical failure _)))))
1231 (call (lexical failure _)))))
1232
1233 ;; Multiple common tests should get lifted as well.
1234 (pass-if-peval
1235 (if (and (struct? x) (eq? (struct-vtable x) A) B)
1236 (foo x)
1237 (if (and (struct? x) (eq? (struct-vtable x) A) C)
1238 (bar x)
1239 (if (and (struct? x) (eq? (struct-vtable x) A) D)
1240 (baz x)
1241 (qux x))))
1242 (let (failure) (_) ((lambda _
1243 (lambda-case
1244 ((() #f #f #f () ())
1245 (call (toplevel qux) (toplevel x))))))
1246 (if (primcall struct? (toplevel x))
1247 (if (primcall eq?
1248 (primcall struct-vtable (toplevel x))
1249 (toplevel A))
1250 (if (toplevel B)
1251 (call (toplevel foo) (toplevel x))
1252 (if (toplevel C)
1253 (call (toplevel bar) (toplevel x))
1254 (if (toplevel D)
1255 (call (toplevel baz) (toplevel x))
1256 (call (lexical failure _)))))
1257 (call (lexical failure _)))
1258 (call (lexical failure _)))))
1259
1260 (pass-if-peval
1261 (apply (lambda (x y) (cons x y)) '(1 2))
1262 (primcall cons (const 1) (const 2)))
1263
1264 (pass-if-peval
1265 (apply (lambda (x y) (cons x y)) (list 1 2))
1266 (primcall cons (const 1) (const 2)))
1267
1268 ;; Disable after removal of abort-in-tail-position optimization, in
1269 ;; hopes that CPS does a uniformly better job.
1270 #;
1271 (pass-if-peval
1272 (let ((t (make-prompt-tag)))
1273 (call-with-prompt t
1274 (lambda () (abort-to-prompt t 1 2 3))
1275 (lambda (k x y z) (list x y z))))
1276 (primcall list (const 1) (const 2) (const 3)))
1277
1278 (pass-if-peval
1279 (call-with-values foo (lambda (x) (bar x)))
1280 (let (x) (_) ((call (toplevel foo)))
1281 (call (toplevel bar) (lexical x _))))
1282
1283 (pass-if-peval
1284 ((lambda (foo)
1285 (define* (bar a #:optional (b (1+ a)))
1286 (list a b))
1287 (bar 1))
1288 1)
1289 (primcall list (const 1) (const 2))))