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