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