Tree-il post-order rewriter no longer destructive
[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 (const -1) ; (f -1 0)
351 (primcall
352 +
353 (const 0) ; (f 1 0)
354 (primcall
355 +
356 (seq (toplevel y) (const -1)) ; (f -1 y)
357 (primcall
358 +
359 (toplevel y) ; (f 2 y)
360 (let (x y) (_ _) ((toplevel z) (toplevel y)) ; (f z y)
361 (if (primcall > (lexical x _) (const 0))
362 (lexical y _)
363 (lexical x _))))))))
364
365 (pass-if-peval
366 ;; First order, conditional.
367 (let ((y 2))
368 (lambda (x)
369 (if (> y 0)
370 (display x)
371 'never-reached)))
372 (lambda ()
373 (lambda-case
374 (((x) #f #f #f () (_))
375 (call (toplevel display) (lexical x _))))))
376
377 (pass-if-peval
378 ;; First order, recursive procedure.
379 (letrec ((fibo (lambda (n)
380 (if (<= n 1)
381 n
382 (+ (fibo (- n 1))
383 (fibo (- n 2)))))))
384 (fibo 4))
385 (const 3))
386
387 (pass-if-peval
388 ;; Don't propagate toplevel references, as intervening expressions
389 ;; could alter their bindings.
390 (let ((x top))
391 (foo)
392 x)
393 (let (x) (_) ((toplevel top))
394 (seq
395 (call (toplevel foo))
396 (lexical x _))))
397
398 (pass-if-peval
399 ;; Higher order.
400 ((lambda (f x)
401 (f (* (car x) (cadr x))))
402 (lambda (x)
403 (+ x 1))
404 '(2 3))
405 (const 7))
406
407 (pass-if-peval
408 ;; Higher order with optional argument (default value).
409 ((lambda* (f x #:optional (y 0))
410 (+ y (f (* (car x) (cadr x)))))
411 (lambda (x)
412 (+ x 1))
413 '(2 3))
414 (const 7))
415
416 (pass-if-peval
417 ;; Higher order with optional argument (caller-supplied value).
418 ((lambda* (f x #:optional (y 0))
419 (+ y (f (* (car x) (cadr x)))))
420 (lambda (x)
421 (+ x 1))
422 '(2 3)
423 35)
424 (const 42))
425
426 (pass-if-peval
427 ;; Higher order with optional argument (side-effecting default
428 ;; value).
429 ((lambda* (f x #:optional (y (foo)))
430 (+ y (f (* (car x) (cadr x)))))
431 (lambda (x)
432 (+ x 1))
433 '(2 3))
434 (let (y) (_) ((call (toplevel foo)))
435 (primcall + (lexical y _) (const 7))))
436
437 (pass-if-peval
438 ;; Higher order with optional argument (caller-supplied value).
439 ((lambda* (f x #:optional (y (foo)))
440 (+ y (f (* (car x) (cadr x)))))
441 (lambda (x)
442 (+ x 1))
443 '(2 3)
444 35)
445 (const 42))
446
447 (pass-if-peval
448 ;; Higher order.
449 ((lambda (f) (f x)) (lambda (x) x))
450 (toplevel x))
451
452 (pass-if-peval
453 ;; Bug reported at
454 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html>.
455 (let ((fold (lambda (f g) (f (g top)))))
456 (fold 1+ (lambda (x) x)))
457 (primcall 1+ (toplevel top)))
458
459 (pass-if-peval
460 ;; Procedure not inlined when residual code contains recursive calls.
461 ;; <http://debbugs.gnu.org/9542>
462 (letrec ((fold (lambda (f x3 b null? car cdr)
463 (if (null? x3)
464 b
465 (f (car x3) (fold f (cdr x3) b null? car cdr))))))
466 (fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
467 (letrec (fold) (_) (_)
468 (call (lexical fold _)
469 (primitive *)
470 (toplevel x)
471 (const 1)
472 (primitive zero?)
473 (lambda ()
474 (lambda-case
475 (((x1) #f #f #f () (_))
476 (lexical x1 _))))
477 (lambda ()
478 (lambda-case
479 (((x2) #f #f #f () (_))
480 (primcall 1- (lexical x2 _))))))))
481
482 (pass-if "inlined lambdas are alpha-renamed"
483 ;; In this example, `make-adder' is inlined more than once; thus,
484 ;; they should use different gensyms for their arguments, because
485 ;; the various optimization passes assume uniquely-named variables.
486 ;;
487 ;; Bug reported at
488 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
489 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
490 (pmatch (unparse-tree-il
491 (peval (expand-primitives!
492 (resolve-primitives
493 (compile
494 '(let ((make-adder
495 (lambda (x) (lambda (y) (+ x y)))))
496 (cons (make-adder 1) (make-adder 2)))
497 #:to 'tree-il)
498 (current-module)))))
499 ((primcall cons
500 (lambda ()
501 (lambda-case
502 (((y) #f #f #f () (,gensym1))
503 (primcall +
504 (const 1)
505 (lexical y ,ref1)))))
506 (lambda ()
507 (lambda-case
508 (((y) #f #f #f () (,gensym2))
509 (primcall +
510 (const 2)
511 (lexical y ,ref2))))))
512 (and (eq? gensym1 ref1)
513 (eq? gensym2 ref2)
514 (not (eq? gensym1 gensym2))))
515 (_ #f)))
516
517 (pass-if-peval
518 ;; Unused letrec bindings are pruned.
519 (letrec ((a (lambda () (b)))
520 (b (lambda () (a)))
521 (c (lambda (x) x)))
522 (c 10))
523 (const 10))
524
525 (pass-if-peval
526 ;; Unused letrec bindings are pruned.
527 (letrec ((a (foo!))
528 (b (lambda () (a)))
529 (c (lambda (x) x)))
530 (c 10))
531 (seq (call (toplevel foo!))
532 (const 10)))
533
534 (pass-if-peval
535 ;; Higher order, mutually recursive procedures.
536 (letrec ((even? (lambda (x)
537 (or (= 0 x)
538 (odd? (- x 1)))))
539 (odd? (lambda (x)
540 (not (even? x)))))
541 (and (even? 4) (odd? 7)))
542 (const #t))
543
544 (pass-if-peval
545 ;; Memv with constants.
546 (memv 1 '(3 2 1))
547 (const '(1)))
548
549 (pass-if-peval
550 ;; Memv with non-constant list. It could fold but doesn't
551 ;; currently.
552 (memv 1 (list 3 2 1))
553 (primcall memv
554 (const 1)
555 (primcall list (const 3) (const 2) (const 1))))
556
557 (pass-if-peval
558 ;; Memv with non-constant key, constant list, test context
559 (case foo
560 ((3 2 1) 'a)
561 (else 'b))
562 (let (key) (_) ((toplevel foo))
563 (if (if (primcall eqv? (lexical key _) (const 3))
564 (const #t)
565 (if (primcall eqv? (lexical key _) (const 2))
566 (const #t)
567 (primcall eqv? (lexical key _) (const 1))))
568 (const a)
569 (const b))))
570
571 (pass-if-peval
572 ;; Memv with non-constant key, empty list, test context.
573 (case foo
574 (() 'a)
575 (else 'b))
576 (seq (toplevel foo) (const 'b)))
577
578 ;;
579 ;; Below are cases where constant propagation should bail out.
580 ;;
581
582 (pass-if-peval
583 ;; Non-constant lexical is not propagated.
584 (let ((v (make-vector 6 #f)))
585 (lambda (n)
586 (vector-set! v n n)))
587 (let (v) (_)
588 ((call (toplevel make-vector) (const 6) (const #f)))
589 (lambda ()
590 (lambda-case
591 (((n) #f #f #f () (_))
592 (primcall vector-set!
593 (lexical v _) (lexical n _) (lexical n _)))))))
594
595 (pass-if-peval
596 ;; Mutable lexical is not propagated.
597 (let ((v (vector 1 2 3)))
598 (lambda ()
599 v))
600 (let (v) (_)
601 ((primcall vector (const 1) (const 2) (const 3)))
602 (lambda ()
603 (lambda-case
604 ((() #f #f #f () ())
605 (lexical v _))))))
606
607 (pass-if-peval
608 ;; Lexical that is not provably pure is not inlined nor propagated.
609 (let* ((x (if (> p q) (frob!) (display 'chbouib)))
610 (y (* x 2)))
611 (+ x x y))
612 (let (x) (_) ((if (primcall > (toplevel p) (toplevel q))
613 (call (toplevel frob!))
614 (call (toplevel display) (const chbouib))))
615 (let (y) (_) ((primcall * (lexical x _) (const 2)))
616 (primcall +
617 (lexical x _)
618 (primcall + (lexical x _) (lexical y _))))))
619
620 (pass-if-peval
621 ;; Non-constant arguments not propagated to lambdas.
622 ((lambda (x y z)
623 (vector-set! x 0 0)
624 (set-car! y 0)
625 (set-cdr! z '()))
626 (vector 1 2 3)
627 (make-list 10)
628 (list 1 2 3))
629 (let (x y z) (_ _ _)
630 ((primcall vector (const 1) (const 2) (const 3))
631 (call (toplevel make-list) (const 10))
632 (primcall list (const 1) (const 2) (const 3)))
633 (seq
634 (primcall vector-set!
635 (lexical x _) (const 0) (const 0))
636 (seq (primcall set-car!
637 (lexical y _) (const 0))
638 (primcall set-cdr!
639 (lexical z _) (const ()))))))
640
641 (pass-if-peval
642 (let ((foo top-foo) (bar top-bar))
643 (let* ((g (lambda (x y) (+ x y)))
644 (f (lambda (g x) (g x x))))
645 (+ (f g foo) (f g bar))))
646 (let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
647 (primcall +
648 (primcall + (lexical foo _) (lexical foo _))
649 (primcall + (lexical bar _) (lexical bar _)))))
650
651 (pass-if-peval
652 ;; Fresh objects are not turned into constants, nor are constants
653 ;; turned into fresh objects.
654 (let* ((c '(2 3))
655 (x (cons 1 c))
656 (y (cons 0 x)))
657 y)
658 (let (x) (_) ((primcall cons (const 1) (const (2 3))))
659 (primcall cons (const 0) (lexical x _))))
660
661 (pass-if-peval
662 ;; Bindings mutated.
663 (let ((x 2))
664 (set! x 3)
665 x)
666 (let (x) (_) ((const 2))
667 (seq
668 (set! (lexical x _) (const 3))
669 (lexical x _))))
670
671 (pass-if-peval
672 ;; Bindings mutated.
673 (letrec ((x 0)
674 (f (lambda ()
675 (set! x (+ 1 x))
676 x)))
677 (frob f) ; may mutate `x'
678 x)
679 (letrec (x) (_) ((const 0))
680 (seq
681 (call (toplevel frob) (lambda _ _))
682 (lexical x _))))
683
684 (pass-if-peval
685 ;; Bindings mutated.
686 (letrec ((f (lambda (x)
687 (set! f (lambda (_) x))
688 x)))
689 (f 2))
690 (letrec _ . _))
691
692 (pass-if-peval
693 ;; Bindings possibly mutated.
694 (let ((x (make-foo)))
695 (frob! x) ; may mutate `x'
696 x)
697 (let (x) (_) ((call (toplevel make-foo)))
698 (seq
699 (call (toplevel frob!) (lexical x _))
700 (lexical x _))))
701
702 (pass-if-peval
703 ;; Inlining stops at recursive calls with dynamic arguments.
704 (let loop ((x x))
705 (if (< x 0) x (loop (1- x))))
706 (letrec (loop) (_) ((lambda (_)
707 (lambda-case
708 (((x) #f #f #f () (_))
709 (if _ _
710 (call (lexical loop _)
711 (primcall 1-
712 (lexical x _))))))))
713 (call (lexical loop _) (toplevel x))))
714
715 (pass-if-peval
716 ;; Recursion on the 2nd argument is fully evaluated.
717 (let ((x (top)))
718 (let loop ((x x) (y 10))
719 (if (> y 0)
720 (loop x (1- y))
721 (foo x y))))
722 (let (x) (_) ((call (toplevel top)))
723 (call (toplevel foo) (lexical x _) (const 0))))
724
725 (pass-if-peval
726 ;; Inlining aborted when residual code contains recursive calls.
727 ;;
728 ;; <http://debbugs.gnu.org/9542>
729 (let loop ((x x) (y 0))
730 (if (> y 0)
731 (loop (1- x) (1- y))
732 (if (< x 0)
733 x
734 (loop (1+ x) (1+ y)))))
735 (letrec (loop) (_) ((lambda (_)
736 (lambda-case
737 (((x y) #f #f #f () (_ _))
738 (if (primcall >
739 (lexical y _) (const 0))
740 _ _)))))
741 (call (lexical loop _) (toplevel x) (const 0))))
742
743 (pass-if-peval
744 ;; Infinite recursion: `peval' gives up and leaves it as is.
745 (letrec ((f (lambda (x) (g (1- x))))
746 (g (lambda (x) (h (1+ x))))
747 (h (lambda (x) (f x))))
748 (f 0))
749 (letrec _ . _))
750
751 (pass-if-peval
752 ;; Infinite recursion: all the arguments to `loop' are static, but
753 ;; unrolling it would lead `peval' to enter an infinite loop.
754 (let loop ((x 0))
755 (and (< x top)
756 (loop (1+ x))))
757 (letrec (loop) (_) ((lambda . _))
758 (call (lexical loop _) (const 0))))
759
760 (pass-if-peval
761 ;; This test checks that the `start' binding is indeed residualized.
762 ;; See the `referenced?' procedure in peval's `prune-bindings'.
763 (let ((pos 0))
764 (let ((here (let ((start pos)) (lambda () start))))
765 (set! pos 1) ;; Cause references to `pos' to residualize.
766 (here)))
767 (let (pos) (_) ((const 0))
768 (let (here) (_) (_)
769 (seq
770 (set! (lexical pos _) (const 1))
771 (call (lexical here _))))))
772
773 (pass-if-peval
774 ;; FIXME: should this one residualize the binding?
775 (letrec ((a a))
776 1)
777 (const 1))
778
779 (pass-if-peval
780 ;; This is a fun one for peval to handle.
781 (letrec ((a a))
782 a)
783 (letrec (a) (_) ((lexical a _))
784 (lexical a _)))
785
786 (pass-if-peval
787 ;; Another interesting recursive case.
788 (letrec ((a b) (b a))
789 a)
790 (letrec (a) (_) ((lexical a _))
791 (lexical a _)))
792
793 (pass-if-peval
794 ;; Another pruning case, that `a' is residualized.
795 (letrec ((a (lambda () (a)))
796 (b (lambda () (a)))
797 (c (lambda (x) x)))
798 (let ((d (foo b)))
799 (c d)))
800
801 ;; "b c a" is the current order that we get with unordered letrec,
802 ;; but it's not important to this test, so if it changes, just adapt
803 ;; the test.
804 (letrec (b c a) (_ _ _)
805 ((lambda _
806 (lambda-case
807 ((() #f #f #f () ())
808 (call (lexical a _)))))
809 (lambda _
810 (lambda-case
811 (((x) #f #f #f () (_))
812 (lexical x _))))
813 (lambda _
814 (lambda-case
815 ((() #f #f #f () ())
816 (call (lexical a _))))))
817 (let (d)
818 (_)
819 ((call (toplevel foo) (lexical b _)))
820 (call (lexical c _) (lexical d _)))))
821
822 (pass-if-peval
823 ;; In this case, we can prune the bindings. `a' ends up being copied
824 ;; because it is only referenced once in the source program. Oh
825 ;; well.
826 (letrec* ((a (lambda (x) (top x)))
827 (b (lambda () a)))
828 (foo (b) (b)))
829 (call (toplevel foo)
830 (lambda _
831 (lambda-case
832 (((x) #f #f #f () (_))
833 (call (toplevel top) (lexical x _)))))
834 (lambda _
835 (lambda-case
836 (((x) #f #f #f () (_))
837 (call (toplevel top) (lexical x _)))))))
838
839 (pass-if-peval
840 ;; The inliner sees through a `let'.
841 ((let ((a 10)) (lambda (b) (* b 2))) 30)
842 (const 60))
843
844 (pass-if-peval
845 ((lambda ()
846 (define (const x) (lambda (_) x))
847 (let ((v #f))
848 ((const #t) v))))
849 (const #t))
850
851 (pass-if-peval
852 ;; Applications of procedures with rest arguments can get inlined.
853 ((lambda (x y . z)
854 (list x y z))
855 1 2 3 4)
856 (let (z) (_) ((primcall list (const 3) (const 4)))
857 (primcall list (const 1) (const 2) (lexical z _))))
858
859 (pass-if-peval
860 ;; Unmutated lists can get inlined.
861 (let ((args (list 2 3)))
862 (apply (lambda (x y z w)
863 (list x y z w))
864 0 1 args))
865 (primcall list (const 0) (const 1) (const 2) (const 3)))
866
867 (pass-if-peval
868 ;; However if the list might have been mutated, it doesn't propagate.
869 (let ((args (list 2 3)))
870 (foo! args)
871 (apply (lambda (x y z w)
872 (list x y z w))
873 0 1 args))
874 (let (args) (_) ((primcall list (const 2) (const 3)))
875 (seq
876 (call (toplevel foo!) (lexical args _))
877 (primcall @apply
878 (lambda ()
879 (lambda-case
880 (((x y z w) #f #f #f () (_ _ _ _))
881 (primcall list
882 (lexical x _) (lexical y _)
883 (lexical z _) (lexical w _)))))
884 (const 0)
885 (const 1)
886 (lexical args _)))))
887
888 (pass-if-peval
889 ;; Here the `args' that gets built by the application of the lambda
890 ;; takes more than effort "10" to visit. Test that we fall back to
891 ;; the source expression of the operand, which is still a call to
892 ;; `list', so the inlining still happens.
893 (lambda (bv offset n)
894 (let ((x (bytevector-ieee-single-native-ref
895 bv
896 (+ offset 0)))
897 (y (bytevector-ieee-single-native-ref
898 bv
899 (+ offset 4))))
900 (let ((args (list x y)))
901 (@apply
902 (lambda (bv offset x y)
903 (bytevector-ieee-single-native-set!
904 bv
905 (+ offset 0)
906 x)
907 (bytevector-ieee-single-native-set!
908 bv
909 (+ offset 4)
910 y))
911 bv
912 offset
913 args))))
914 (lambda ()
915 (lambda-case
916 (((bv offset n) #f #f #f () (_ _ _))
917 (let (x y) (_ _) ((primcall bytevector-ieee-single-native-ref
918 (lexical bv _)
919 (primcall +
920 (lexical offset _) (const 0)))
921 (primcall bytevector-ieee-single-native-ref
922 (lexical bv _)
923 (primcall +
924 (lexical offset _) (const 4))))
925 (seq
926 (primcall bytevector-ieee-single-native-set!
927 (lexical bv _)
928 (primcall +
929 (lexical offset _) (const 0))
930 (lexical x _))
931 (primcall bytevector-ieee-single-native-set!
932 (lexical bv _)
933 (primcall +
934 (lexical offset _) (const 4))
935 (lexical y _))))))))
936
937 (pass-if-peval
938 ;; Here we ensure that non-constant expressions are not copied.
939 (lambda ()
940 (let ((args (list (foo!))))
941 (@apply
942 (lambda (z x)
943 (list z x))
944 ;; This toplevel ref might raise an unbound variable exception.
945 ;; The effects of `(foo!)' must be visible before this effect.
946 z
947 args)))
948 (lambda ()
949 (lambda-case
950 ((() #f #f #f () ())
951 (let (_) (_) ((call (toplevel foo!)))
952 (let (z) (_) ((toplevel z))
953 (primcall 'list
954 (lexical z _)
955 (lexical _ _))))))))
956
957 (pass-if-peval
958 ;; Rest args referenced more than once are not destructured.
959 (lambda ()
960 (let ((args (list 'foo)))
961 (set-car! args 'bar)
962 (@apply
963 (lambda (z x)
964 (list z x))
965 z
966 args)))
967 (lambda ()
968 (lambda-case
969 ((() #f #f #f () ())
970 (let (args) (_)
971 ((primcall list (const foo)))
972 (seq
973 (primcall set-car! (lexical args _) (const bar))
974 (primcall @apply
975 (lambda . _)
976 (toplevel z)
977 (lexical args _))))))))
978
979 (pass-if-peval
980 ;; Let-values inlining, even with consumers with rest args.
981 (call-with-values (lambda () (values 1 2))
982 (lambda args
983 (apply list args)))
984 (primcall list (const 1) (const 2)))
985
986 (pass-if-peval
987 ;; Constant folding: cons of #nil does not make list
988 (cons 1 #nil)
989 (primcall cons (const 1) (const '#nil)))
990
991 (pass-if-peval
992 ;; Constant folding: cons
993 (begin (cons 1 2) #f)
994 (const #f))
995
996 (pass-if-peval
997 ;; Constant folding: cons
998 (begin (cons (foo) 2) #f)
999 (seq (call (toplevel foo)) (const #f)))
1000
1001 (pass-if-peval
1002 ;; Constant folding: cons
1003 (if (cons 0 0) 1 2)
1004 (const 1))
1005
1006 (pass-if-peval
1007 ;; Constant folding: car+cons
1008 (car (cons 1 0))
1009 (const 1))
1010
1011 (pass-if-peval
1012 ;; Constant folding: cdr+cons
1013 (cdr (cons 1 0))
1014 (const 0))
1015
1016 (pass-if-peval
1017 ;; Constant folding: car+cons, impure
1018 (car (cons 1 (bar)))
1019 (seq (call (toplevel bar)) (const 1)))
1020
1021 (pass-if-peval
1022 ;; Constant folding: cdr+cons, impure
1023 (cdr (cons (bar) 0))
1024 (seq (call (toplevel bar)) (const 0)))
1025
1026 (pass-if-peval
1027 ;; Constant folding: car+list
1028 (car (list 1 0))
1029 (const 1))
1030
1031 (pass-if-peval
1032 ;; Constant folding: cdr+list
1033 (cdr (list 1 0))
1034 (primcall list (const 0)))
1035
1036 (pass-if-peval
1037 ;; Constant folding: car+list, impure
1038 (car (list 1 (bar)))
1039 (seq (call (toplevel bar)) (const 1)))
1040
1041 (pass-if-peval
1042 ;; Constant folding: cdr+list, impure
1043 (cdr (list (bar) 0))
1044 (seq (call (toplevel bar)) (primcall list (const 0))))
1045
1046 (pass-if-peval
1047 ;; Equality primitive: same lexical
1048 (let ((x (random))) (eq? x x))
1049 (seq (call (toplevel random)) (const #t)))
1050
1051 (pass-if-peval
1052 ;; Equality primitive: merge lexical identities
1053 (let* ((x (random)) (y x)) (eq? x y))
1054 (seq (call (toplevel random)) (const #t)))
1055
1056 (pass-if-peval
1057 ;; Non-constant guards get lexical bindings.
1058 (dynamic-wind foo (lambda () bar) baz)
1059 (let (w u) (_ _) ((toplevel foo) (toplevel baz))
1060 (dynwind (lexical w _)
1061 (call (lexical w _))
1062 (toplevel bar)
1063 (call (lexical u _))
1064 (lexical u _))))
1065
1066 (pass-if-peval
1067 ;; Constant guards don't need lexical bindings.
1068 (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
1069 (dynwind
1070 (lambda ()
1071 (lambda-case
1072 ((() #f #f #f () ()) (toplevel foo))))
1073 (toplevel foo)
1074 (toplevel bar)
1075 (toplevel baz)
1076 (lambda ()
1077 (lambda-case
1078 ((() #f #f #f () ()) (toplevel baz))))))
1079
1080 (pass-if-peval
1081 ;; Prompt is removed if tag is unreferenced
1082 (let ((tag (make-prompt-tag)))
1083 (call-with-prompt tag
1084 (lambda () 1)
1085 (lambda args args)))
1086 (const 1))
1087
1088 (pass-if-peval
1089 ;; Prompt is removed if tag is unreferenced, with explicit stem
1090 (let ((tag (make-prompt-tag "foo")))
1091 (call-with-prompt tag
1092 (lambda () 1)
1093 (lambda args args)))
1094 (const 1))
1095
1096 ;; Handler lambda inlined
1097 (pass-if-peval
1098 (call-with-prompt tag
1099 (lambda () 1)
1100 (lambda (k x) x))
1101 (prompt (toplevel tag)
1102 (const 1)
1103 (lambda-case
1104 (((k x) #f #f #f () (_ _))
1105 (lexical x _)))))
1106
1107 ;; Handler toplevel not inlined
1108 (pass-if-peval
1109 (call-with-prompt tag
1110 (lambda () 1)
1111 handler)
1112 (let (handler) (_) ((toplevel handler))
1113 (prompt (toplevel tag)
1114 (const 1)
1115 (lambda-case
1116 ((() #f args #f () (_))
1117 (primcall @apply
1118 (lexical handler _)
1119 (lexical args _)))))))
1120
1121 (pass-if-peval
1122 ;; `while' without `break' or `continue' has no prompts and gets its
1123 ;; condition folded. Unfortunately the outer `lp' does not yet get
1124 ;; elided, and the continuation tag stays around. (The continue tag
1125 ;; stays around because although it is not referenced, recursively
1126 ;; visiting the loop in the continue handler manages to visit the tag
1127 ;; twice before aborting. The abort doesn't unroll the recursive
1128 ;; reference.)
1129 (while #t #t)
1130 (let (_) (_) ((primcall make-prompt-tag . _))
1131 (letrec (lp) (_)
1132 ((lambda _
1133 (lambda-case
1134 ((() #f #f #f () ())
1135 (letrec (loop) (_)
1136 ((lambda _
1137 (lambda-case
1138 ((() #f #f #f () ())
1139 (call (lexical loop _))))))
1140 (call (lexical loop _)))))))
1141 (call (lexical lp _)))))
1142
1143 (pass-if-peval
1144 (lambda (a . rest)
1145 (apply (lambda (x y) (+ x y))
1146 a rest))
1147 (lambda _
1148 (lambda-case
1149 (((x y) #f #f #f () (_ _))
1150 _))))
1151
1152 (pass-if-peval
1153 (car '(1 2))
1154 (const 1))
1155
1156 ;; If we bail out when inlining an identifier because it's too big,
1157 ;; but the identifier simply aliases some other identifier, then avoid
1158 ;; residualizing a reference to the leaf identifier. The bailout is
1159 ;; driven by the recursive-effort-limit, which is currently 100. We
1160 ;; make sure to trip it with this recursive sum thing.
1161 (pass-if-peval
1162 (let ((x (let sum ((n 0) (out 0))
1163 (if (< n 10000)
1164 (sum (1+ n) (+ out n))
1165 out))))
1166 ((lambda (y) (list y)) x))
1167 (let (x) (_) (_)
1168 (primcall list (lexical x _))))
1169
1170 ;; Here we test that a common test in a chain of ifs gets lifted.
1171 (pass-if-peval
1172 (if (and (struct? x) (eq? (struct-vtable x) A))
1173 (foo x)
1174 (if (and (struct? x) (eq? (struct-vtable x) B))
1175 (bar x)
1176 (if (and (struct? x) (eq? (struct-vtable x) C))
1177 (baz x)
1178 (qux x))))
1179 (let (failure) (_) ((lambda _
1180 (lambda-case
1181 ((() #f #f #f () ())
1182 (call (toplevel qux) (toplevel x))))))
1183 (if (primcall struct? (toplevel x))
1184 (if (primcall eq?
1185 (primcall struct-vtable (toplevel x))
1186 (toplevel A))
1187 (call (toplevel foo) (toplevel x))
1188 (if (primcall eq?
1189 (primcall struct-vtable (toplevel x))
1190 (toplevel B))
1191 (call (toplevel bar) (toplevel x))
1192 (if (primcall eq?
1193 (primcall struct-vtable (toplevel x))
1194 (toplevel C))
1195 (call (toplevel baz) (toplevel x))
1196 (call (lexical failure _)))))
1197 (call (lexical failure _)))))
1198
1199 ;; Multiple common tests should get lifted as well.
1200 (pass-if-peval
1201 (if (and (struct? x) (eq? (struct-vtable x) A) B)
1202 (foo x)
1203 (if (and (struct? x) (eq? (struct-vtable x) A) C)
1204 (bar x)
1205 (if (and (struct? x) (eq? (struct-vtable x) A) D)
1206 (baz x)
1207 (qux x))))
1208 (let (failure) (_) ((lambda _
1209 (lambda-case
1210 ((() #f #f #f () ())
1211 (call (toplevel qux) (toplevel x))))))
1212 (if (primcall struct? (toplevel x))
1213 (if (primcall eq?
1214 (primcall struct-vtable (toplevel x))
1215 (toplevel A))
1216 (if (toplevel B)
1217 (call (toplevel foo) (toplevel x))
1218 (if (toplevel C)
1219 (call (toplevel bar) (toplevel x))
1220 (if (toplevel D)
1221 (call (toplevel baz) (toplevel x))
1222 (call (lexical failure _)))))
1223 (call (lexical failure _)))
1224 (call (lexical failure _)))))
1225
1226 (pass-if-peval
1227 (apply (lambda (x y) (cons x y)) '(1 2))
1228 (primcall cons (const 1) (const 2)))
1229
1230 (pass-if-peval
1231 (apply (lambda (x y) (cons x y)) (list 1 2))
1232 (primcall cons (const 1) (const 2)))
1233
1234 (pass-if-peval
1235 (let ((t (make-prompt-tag)))
1236 (call-with-prompt t
1237 (lambda () (abort-to-prompt t 1 2 3))
1238 (lambda (k x y z) (list x y z))))
1239 (primcall list (const 1) (const 2) (const 3)))
1240
1241 (pass-if-peval
1242 (call-with-values foo (lambda (x) (bar x)))
1243 (let (x) (_) ((call (toplevel foo)))
1244 (call (toplevel bar) (lexical x _)))))