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