Merge remote-tracking branch 'origin/stable-2.0'
[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;;;;
4;;;; Copyright (C) 2009, 2010, 2011, 2012 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 (srfi srfi-13))
29
30(define peval
31 ;; The partial evaluator.
32 (@@ (language tree-il optimize) peval))
33
34(define-syntax pass-if-peval
35 (syntax-rules (resolve-primitives)
36 ((_ in pat)
de1eb420
AW
37 (pass-if-peval in pat
38 (expand-primitives!
39 (resolve-primitives!
40 (compile 'in #:from 'scheme #:to 'tree-il)
41 (current-module)))))
42 ((_ in pat code)
43 (pass-if 'in
44 (let ((evaled (unparse-tree-il (peval code))))
45 (pmatch evaled
46 (pat #t)
47 (_ (pk 'peval-mismatch)
48 ((@ (ice-9 pretty-print) pretty-print)
49 'in)
50 (newline)
51 ((@ (ice-9 pretty-print) pretty-print)
52 evaled)
53 (newline)
54 ((@ (ice-9 pretty-print) pretty-print)
55 'pat)
56 (newline)
57 #f)))))))
58
59\f
60(with-test-prefix "partial evaluation"
61
62 (pass-if-peval
63 ;; First order, primitive.
64 (let ((x 1) (y 2)) (+ x y))
65 (const 3))
66
67 (pass-if-peval
68 ;; First order, thunk.
69 (let ((x 1) (y 2))
70 (let ((f (lambda () (+ x y))))
71 (f)))
72 (const 3))
73
c46e0a8a 74 (pass-if-peval
de1eb420
AW
75 ;; First order, let-values (requires primitive expansion for
76 ;; `call-with-values'.)
77 (let ((x 0))
78 (call-with-values
79 (lambda () (if (zero? x) (values 1 2) (values 3 4)))
80 (lambda (a b)
81 (+ a b))))
82 (const 3))
83
c46e0a8a 84 (pass-if-peval
de1eb420
AW
85 ;; First order, multiple values.
86 (let ((x 1) (y 2))
87 (values x y))
c46e0a8a 88 (primcall values (const 1) (const 2)))
de1eb420 89
c46e0a8a 90 (pass-if-peval
de1eb420
AW
91 ;; First order, multiple values truncated.
92 (let ((x (values 1 'a)) (y 2))
93 (values x y))
c46e0a8a 94 (primcall values (const 1) (const 2)))
de1eb420 95
c46e0a8a 96 (pass-if-peval
de1eb420
AW
97 ;; First order, multiple values truncated.
98 (or (values 1 2) 3)
99 (const 1))
100
101 (pass-if-peval
102 ;; First order, coalesced, mutability preserved.
103 (cons 0 (cons 1 (cons 2 (list 3 4 5))))
c46e0a8a
AW
104 (primcall list
105 (const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
de1eb420
AW
106
107 (pass-if-peval
108 ;; First order, coalesced, immutability preserved.
109 (cons 0 (cons 1 (cons 2 '(3 4 5))))
c46e0a8a
AW
110 (primcall cons (const 0)
111 (primcall cons (const 1)
112 (primcall cons (const 2)
113 (const (3 4 5))))))
de1eb420
AW
114
115 ;; These two tests doesn't work any more because we changed the way we
116 ;; deal with constants -- now the algorithm will see a construction as
117 ;; being bound to the lexical, so it won't propagate it. It can't
118 ;; even propagate it in the case that it is only referenced once,
119 ;; because:
120 ;;
121 ;; (let ((x (cons 1 2))) (lambda () x))
122 ;;
123 ;; is not the same as
124 ;;
125 ;; (lambda () (cons 1 2))
126 ;;
127 ;; Perhaps if we determined that not only was it only referenced once,
128 ;; it was not closed over by a lambda, then we could propagate it, and
129 ;; re-enable these two tests.
130 ;;
131 #;
132 (pass-if-peval
133 ;; First order, mutability preserved.
134 (let loop ((i 3) (r '()))
135 (if (zero? i)
136 r
137 (loop (1- i) (cons (cons i i) r))))
c46e0a8a
AW
138 (primcall list
139 (primcall cons (const 1) (const 1))
140 (primcall cons (const 2) (const 2))
141 (primcall cons (const 3) (const 3))))
de1eb420
AW
142 ;;
143 ;; See above.
144 #;
145 (pass-if-peval
146 ;; First order, evaluated.
147 (let loop ((i 7)
148 (r '()))
149 (if (<= i 0)
150 (car r)
151 (loop (1- i) (cons i r))))
152 (const 1))
153
154 ;; Instead here are tests for what happens for the above cases: they
155 ;; unroll but they don't fold.
156 (pass-if-peval
157 (let loop ((i 3) (r '()))
158 (if (zero? i)
159 r
160 (loop (1- i) (cons (cons i i) r))))
161 (let (r) (_)
c46e0a8a
AW
162 ((primcall list
163 (primcall cons (const 3) (const 3))))
de1eb420 164 (let (r) (_)
c46e0a8a
AW
165 ((primcall cons
166 (primcall cons (const 2) (const 2))
167 (lexical r _)))
168 (primcall cons
169 (primcall cons (const 1) (const 1))
170 (lexical r _)))))
de1eb420
AW
171
172 ;; See above.
173 (pass-if-peval
174 (let loop ((i 4)
175 (r '()))
176 (if (<= i 0)
177 (car r)
178 (loop (1- i) (cons i r))))
179 (let (r) (_)
c46e0a8a 180 ((primcall list (const 4)))
de1eb420 181 (let (r) (_)
c46e0a8a
AW
182 ((primcall cons
183 (const 3)
184 (lexical r _)))
de1eb420 185 (let (r) (_)
c46e0a8a
AW
186 ((primcall cons
187 (const 2)
188 (lexical r _)))
de1eb420 189 (let (r) (_)
c46e0a8a
AW
190 ((primcall cons
191 (const 1)
192 (lexical r _)))
193 (primcall car
194 (lexical r _)))))))
de1eb420
AW
195
196 ;; Static sums.
197 (pass-if-peval
198 (let loop ((l '(1 2 3 4)) (sum 0))
199 (if (null? l)
200 sum
201 (loop (cdr l) (+ sum (car l)))))
202 (const 10))
203
c46e0a8a 204 (pass-if-peval
de1eb420
AW
205 (let ((string->chars
206 (lambda (s)
207 (define (char-at n)
208 (string-ref s n))
209 (define (len)
210 (string-length s))
211 (let loop ((i 0))
212 (if (< i (len))
213 (cons (char-at i)
214 (loop (1+ i)))
215 '())))))
216 (string->chars "yo"))
c46e0a8a 217 (primcall list (const #\y) (const #\o)))
de1eb420
AW
218
219 (pass-if-peval
220 ;; Primitives in module-refs are resolved (the expansion of `pmatch'
221 ;; below leads to calls to (@@ (system base pmatch) car) and
222 ;; similar, which is what we want to be inlined.)
223 (begin
224 (use-modules (system base pmatch))
225 (pmatch '(a b c d)
226 ((a b . _)
227 #t)))
c46e0a8a
AW
228 (seq (call . _)
229 (const #t)))
de1eb420
AW
230
231 (pass-if-peval
232 ;; Mutability preserved.
233 ((lambda (x y z) (list x y z)) 1 2 3)
c46e0a8a 234 (primcall list (const 1) (const 2) (const 3)))
de1eb420
AW
235
236 (pass-if-peval
237 ;; Don't propagate effect-free expressions that operate on mutable
238 ;; objects.
239 (let* ((x (list 1))
240 (y (car x)))
241 (set-car! x 0)
242 y)
c46e0a8a
AW
243 (let (x) (_) ((primcall list (const 1)))
244 (let (y) (_) ((primcall car (lexical x _)))
245 (seq
246 (primcall set-car! (lexical x _) (const 0))
de1eb420
AW
247 (lexical y _)))))
248
249 (pass-if-peval
250 ;; Don't propagate effect-free expressions that operate on objects we
251 ;; don't know about.
252 (let ((y (car x)))
253 (set-car! x 0)
254 y)
c46e0a8a
AW
255 (let (y) (_) ((primcall car (toplevel x)))
256 (seq
257 (primcall set-car! (toplevel x) (const 0))
de1eb420
AW
258 (lexical y _))))
259
260 (pass-if-peval
261 ;; Infinite recursion
262 ((lambda (x) (x x)) (lambda (x) (x x)))
263 (let (x) (_)
264 ((lambda _
265 (lambda-case
266 (((x) _ _ _ _ _)
c46e0a8a
AW
267 (call (lexical x _) (lexical x _))))))
268 (call (lexical x _) (lexical x _))))
de1eb420
AW
269
270 (pass-if-peval
271 ;; First order, aliased primitive.
272 (let* ((x *) (y (x 1 2))) y)
273 (const 2))
274
275 (pass-if-peval
276 ;; First order, shadowed primitive.
277 (begin
278 (define (+ x y) (pk x y))
279 (+ 1 2))
c46e0a8a 280 (seq
de1eb420
AW
281 (define +
282 (lambda (_)
283 (lambda-case
284 (((x y) #f #f #f () (_ _))
c46e0a8a
AW
285 (call (toplevel pk) (lexical x _) (lexical y _))))))
286 (call (toplevel +) (const 1) (const 2))))
de1eb420
AW
287
288 (pass-if-peval
289 ;; First-order, effects preserved.
290 (let ((x 2))
291 (do-something!)
292 x)
c46e0a8a
AW
293 (seq
294 (call (toplevel do-something!))
de1eb420
AW
295 (const 2)))
296
297 (pass-if-peval
298 ;; First order, residual bindings removed.
299 (let ((x 2) (y 3))
300 (* (+ x y) z))
c46e0a8a 301 (primcall * (const 5) (toplevel z)))
de1eb420
AW
302
303 (pass-if-peval
304 ;; First order, with lambda.
305 (define (foo x)
306 (define (bar z) (* z z))
307 (+ x (bar 3)))
308 (define foo
309 (lambda (_)
310 (lambda-case
311 (((x) #f #f #f () (_))
c46e0a8a 312 (primcall + (lexical x _) (const 9)))))))
de1eb420
AW
313
314 (pass-if-peval
315 ;; First order, with lambda inlined & specialized twice.
316 (let ((f (lambda (x y)
317 (+ (* x top) y)))
318 (x 2)
319 (y 3))
320 (+ (* x (f x y))
321 (f something x)))
c46e0a8a
AW
322 (primcall +
323 (primcall *
324 (const 2)
325 (primcall + ; (f 2 3)
326 (primcall *
327 (const 2)
328 (toplevel top))
329 (const 3)))
330 (let (x) (_) ((toplevel something)) ; (f something 2)
331 ;; `something' is not const, so preserve order of
332 ;; effects with a lexical binding.
333 (primcall +
334 (primcall *
335 (lexical x _)
336 (toplevel top))
337 (const 2)))))
de1eb420
AW
338
339 (pass-if-peval
340 ;; First order, with lambda inlined & specialized 3 times.
341 (let ((f (lambda (x y) (if (> x 0) y x))))
342 (+ (f -1 0)
343 (f 1 0)
344 (f -1 y)
345 (f 2 y)
346 (f z y)))
c46e0a8a
AW
347 (primcall
348 +
349 (const -1) ; (f -1 0)
350 (primcall
351 +
352 (const 0) ; (f 1 0)
353 (primcall
354 +
355 (seq (toplevel y) (const -1)) ; (f -1 y)
356 (primcall
357 +
358 (toplevel y) ; (f 2 y)
359 (let (x y) (_ _) ((toplevel z) (toplevel y)) ; (f z y)
360 (if (primcall > (lexical x _) (const 0))
361 (lexical y _)
362 (lexical x _))))))))
de1eb420
AW
363
364 (pass-if-peval
365 ;; First order, conditional.
366 (let ((y 2))
367 (lambda (x)
368 (if (> y 0)
369 (display x)
370 'never-reached)))
371 (lambda ()
372 (lambda-case
373 (((x) #f #f #f () (_))
c46e0a8a 374 (call (toplevel display) (lexical x _))))))
de1eb420
AW
375
376 (pass-if-peval
377 ;; First order, recursive procedure.
378 (letrec ((fibo (lambda (n)
379 (if (<= n 1)
380 n
381 (+ (fibo (- n 1))
382 (fibo (- n 2)))))))
383 (fibo 4))
384 (const 3))
385
386 (pass-if-peval
387 ;; Don't propagate toplevel references, as intervening expressions
388 ;; could alter their bindings.
389 (let ((x top))
390 (foo)
391 x)
392 (let (x) (_) ((toplevel top))
c46e0a8a
AW
393 (seq
394 (call (toplevel foo))
de1eb420
AW
395 (lexical x _))))
396
397 (pass-if-peval
398 ;; Higher order.
399 ((lambda (f x)
400 (f (* (car x) (cadr x))))
401 (lambda (x)
402 (+ x 1))
403 '(2 3))
404 (const 7))
405
406 (pass-if-peval
407 ;; Higher order with optional argument (default value).
408 ((lambda* (f x #:optional (y 0))
409 (+ y (f (* (car x) (cadr x)))))
410 (lambda (x)
411 (+ x 1))
412 '(2 3))
413 (const 7))
414
415 (pass-if-peval
416 ;; Higher order with optional argument (caller-supplied value).
417 ((lambda* (f x #:optional (y 0))
418 (+ y (f (* (car x) (cadr x)))))
419 (lambda (x)
420 (+ x 1))
421 '(2 3)
422 35)
423 (const 42))
424
425 (pass-if-peval
426 ;; Higher order with optional argument (side-effecting default
427 ;; value).
428 ((lambda* (f x #:optional (y (foo)))
429 (+ y (f (* (car x) (cadr x)))))
430 (lambda (x)
431 (+ x 1))
432 '(2 3))
c46e0a8a
AW
433 (let (y) (_) ((call (toplevel foo)))
434 (primcall + (lexical y _) (const 7))))
de1eb420
AW
435
436 (pass-if-peval
437 ;; Higher order with optional argument (caller-supplied value).
438 ((lambda* (f x #:optional (y (foo)))
439 (+ y (f (* (car x) (cadr x)))))
440 (lambda (x)
441 (+ x 1))
442 '(2 3)
443 35)
444 (const 42))
445
446 (pass-if-peval
447 ;; Higher order.
448 ((lambda (f) (f x)) (lambda (x) x))
449 (toplevel x))
450
451 (pass-if-peval
452 ;; Bug reported at
453 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html>.
454 (let ((fold (lambda (f g) (f (g top)))))
455 (fold 1+ (lambda (x) x)))
c46e0a8a 456 (primcall 1+ (toplevel top)))
de1eb420
AW
457
458 (pass-if-peval
459 ;; Procedure not inlined when residual code contains recursive calls.
460 ;; <http://debbugs.gnu.org/9542>
461 (letrec ((fold (lambda (f x3 b null? car cdr)
462 (if (null? x3)
463 b
464 (f (car x3) (fold f (cdr x3) b null? car cdr))))))
465 (fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
466 (letrec (fold) (_) (_)
c46e0a8a 467 (call (lexical fold _)
de1eb420
AW
468 (primitive *)
469 (toplevel x)
470 (const 1)
471 (primitive zero?)
472 (lambda ()
473 (lambda-case
474 (((x1) #f #f #f () (_))
475 (lexical x1 _))))
476 (lambda ()
477 (lambda-case
478 (((x2) #f #f #f () (_))
c46e0a8a 479 (primcall 1- (lexical x2 _))))))))
de1eb420
AW
480
481 (pass-if "inlined lambdas are alpha-renamed"
482 ;; In this example, `make-adder' is inlined more than once; thus,
483 ;; they should use different gensyms for their arguments, because
484 ;; the various optimization passes assume uniquely-named variables.
485 ;;
486 ;; Bug reported at
487 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
488 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
489 (pmatch (unparse-tree-il
c46e0a8a
AW
490 (peval (expand-primitives!
491 (resolve-primitives!
492 (compile
493 '(let ((make-adder
494 (lambda (x) (lambda (y) (+ x y)))))
495 (cons (make-adder 1) (make-adder 2)))
496 #:to 'tree-il)
497 (current-module)))))
498 ((primcall cons
499 (lambda ()
500 (lambda-case
501 (((y) #f #f #f () (,gensym1))
502 (primcall +
503 (const 1)
504 (lexical y ,ref1)))))
505 (lambda ()
506 (lambda-case
507 (((y) #f #f #f () (,gensym2))
508 (primcall +
509 (const 2)
510 (lexical y ,ref2))))))
de1eb420
AW
511 (and (eq? gensym1 ref1)
512 (eq? gensym2 ref2)
513 (not (eq? gensym1 gensym2))))
514 (_ #f)))
515
516 (pass-if-peval
517 ;; Unused letrec bindings are pruned.
518 (letrec ((a (lambda () (b)))
519 (b (lambda () (a)))
520 (c (lambda (x) x)))
521 (c 10))
522 (const 10))
523
524 (pass-if-peval
525 ;; Unused letrec bindings are pruned.
526 (letrec ((a (foo!))
527 (b (lambda () (a)))
528 (c (lambda (x) x)))
529 (c 10))
c46e0a8a
AW
530 (seq (call (toplevel foo!))
531 (const 10)))
de1eb420
AW
532
533 (pass-if-peval
534 ;; Higher order, mutually recursive procedures.
535 (letrec ((even? (lambda (x)
536 (or (= 0 x)
537 (odd? (- x 1)))))
538 (odd? (lambda (x)
539 (not (even? x)))))
540 (and (even? 4) (odd? 7)))
541 (const #t))
542
543 (pass-if-peval
544 ;; Memv with constants.
545 (memv 1 '(3 2 1))
546 (const '(1)))
547
548 (pass-if-peval
549 ;; Memv with non-constant list. It could fold but doesn't
550 ;; currently.
551 (memv 1 (list 3 2 1))
c46e0a8a
AW
552 (primcall memv
553 (const 1)
554 (primcall list (const 3) (const 2) (const 1))))
de1eb420
AW
555
556 (pass-if-peval
557 ;; Memv with non-constant key, constant list, test context
558 (case foo
559 ((3 2 1) 'a)
560 (else 'b))
561 (let (key) (_) ((toplevel foo))
c46e0a8a 562 (if (if (primcall eqv? (lexical key _) (const 3))
de1eb420 563 (const #t)
c46e0a8a 564 (if (primcall eqv? (lexical key _) (const 2))
de1eb420 565 (const #t)
c46e0a8a 566 (primcall eqv? (lexical key _) (const 1))))
de1eb420
AW
567 (const a)
568 (const b))))
569
570 (pass-if-peval
c46e0a8a 571 ;; Memv with non-constant key, empty list, test context.
de1eb420
AW
572 (case foo
573 (() 'a)
574 (else 'b))
c46e0a8a 575 (seq (toplevel foo) (const 'b)))
de1eb420
AW
576
577 ;;
578 ;; Below are cases where constant propagation should bail out.
579 ;;
580
581 (pass-if-peval
582 ;; Non-constant lexical is not propagated.
583 (let ((v (make-vector 6 #f)))
584 (lambda (n)
585 (vector-set! v n n)))
586 (let (v) (_)
c46e0a8a 587 ((call (toplevel make-vector) (const 6) (const #f)))
de1eb420
AW
588 (lambda ()
589 (lambda-case
590 (((n) #f #f #f () (_))
c46e0a8a
AW
591 (primcall vector-set!
592 (lexical v _) (lexical n _) (lexical n _)))))))
de1eb420
AW
593
594 (pass-if-peval
595 ;; Mutable lexical is not propagated.
596 (let ((v (vector 1 2 3)))
597 (lambda ()
598 v))
599 (let (v) (_)
c46e0a8a 600 ((primcall vector (const 1) (const 2) (const 3)))
de1eb420
AW
601 (lambda ()
602 (lambda-case
603 ((() #f #f #f () ())
604 (lexical v _))))))
605
606 (pass-if-peval
607 ;; Lexical that is not provably pure is not inlined nor propagated.
608 (let* ((x (if (> p q) (frob!) (display 'chbouib)))
609 (y (* x 2)))
610 (+ x x y))
c46e0a8a
AW
611 (let (x) (_) ((if (primcall > (toplevel p) (toplevel q))
612 (call (toplevel frob!))
613 (call (toplevel display) (const chbouib))))
614 (let (y) (_) ((primcall * (lexical x _) (const 2)))
615 (primcall +
616 (lexical x _)
617 (primcall + (lexical x _) (lexical y _))))))
de1eb420
AW
618
619 (pass-if-peval
620 ;; Non-constant arguments not propagated to lambdas.
621 ((lambda (x y z)
622 (vector-set! x 0 0)
623 (set-car! y 0)
624 (set-cdr! z '()))
625 (vector 1 2 3)
626 (make-list 10)
627 (list 1 2 3))
628 (let (x y z) (_ _ _)
c46e0a8a
AW
629 ((primcall vector (const 1) (const 2) (const 3))
630 (call (toplevel make-list) (const 10))
631 (primcall list (const 1) (const 2) (const 3)))
632 (seq
633 (primcall vector-set!
634 (lexical x _) (const 0) (const 0))
635 (seq (primcall set-car!
636 (lexical y _) (const 0))
637 (primcall set-cdr!
638 (lexical z _) (const ()))))))
de1eb420
AW
639
640 (pass-if-peval
641 (let ((foo top-foo) (bar top-bar))
642 (let* ((g (lambda (x y) (+ x y)))
643 (f (lambda (g x) (g x x))))
644 (+ (f g foo) (f g bar))))
645 (let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
c46e0a8a
AW
646 (primcall +
647 (primcall + (lexical foo _) (lexical foo _))
648 (primcall + (lexical bar _) (lexical bar _)))))
de1eb420
AW
649
650 (pass-if-peval
651 ;; Fresh objects are not turned into constants, nor are constants
652 ;; turned into fresh objects.
653 (let* ((c '(2 3))
654 (x (cons 1 c))
655 (y (cons 0 x)))
656 y)
c46e0a8a
AW
657 (let (x) (_) ((primcall cons (const 1) (const (2 3))))
658 (primcall cons (const 0) (lexical x _))))
de1eb420
AW
659
660 (pass-if-peval
661 ;; Bindings mutated.
662 (let ((x 2))
663 (set! x 3)
664 x)
665 (let (x) (_) ((const 2))
c46e0a8a 666 (seq
de1eb420
AW
667 (set! (lexical x _) (const 3))
668 (lexical x _))))
669
670 (pass-if-peval
671 ;; Bindings mutated.
672 (letrec ((x 0)
673 (f (lambda ()
674 (set! x (+ 1 x))
675 x)))
676 (frob f) ; may mutate `x'
677 x)
678 (letrec (x) (_) ((const 0))
c46e0a8a
AW
679 (seq
680 (call (toplevel frob) (lambda _ _))
de1eb420
AW
681 (lexical x _))))
682
683 (pass-if-peval
684 ;; Bindings mutated.
685 (letrec ((f (lambda (x)
686 (set! f (lambda (_) x))
687 x)))
688 (f 2))
689 (letrec _ . _))
690
691 (pass-if-peval
692 ;; Bindings possibly mutated.
693 (let ((x (make-foo)))
694 (frob! x) ; may mutate `x'
695 x)
c46e0a8a
AW
696 (let (x) (_) ((call (toplevel make-foo)))
697 (seq
698 (call (toplevel frob!) (lexical x _))
de1eb420
AW
699 (lexical x _))))
700
701 (pass-if-peval
702 ;; Inlining stops at recursive calls with dynamic arguments.
703 (let loop ((x x))
704 (if (< x 0) x (loop (1- x))))
705 (letrec (loop) (_) ((lambda (_)
706 (lambda-case
707 (((x) #f #f #f () (_))
708 (if _ _
c46e0a8a
AW
709 (call (lexical loop _)
710 (primcall 1-
711 (lexical x _))))))))
712 (call (lexical loop _) (toplevel x))))
de1eb420
AW
713
714 (pass-if-peval
715 ;; Recursion on the 2nd argument is fully evaluated.
716 (let ((x (top)))
717 (let loop ((x x) (y 10))
718 (if (> y 0)
719 (loop x (1- y))
720 (foo x y))))
c46e0a8a
AW
721 (let (x) (_) ((call (toplevel top)))
722 (call (toplevel foo) (lexical x _) (const 0))))
de1eb420
AW
723
724 (pass-if-peval
725 ;; Inlining aborted when residual code contains recursive calls.
726 ;;
727 ;; <http://debbugs.gnu.org/9542>
728 (let loop ((x x) (y 0))
729 (if (> y 0)
730 (loop (1- x) (1- y))
731 (if (< x 0)
732 x
733 (loop (1+ x) (1+ y)))))
734 (letrec (loop) (_) ((lambda (_)
735 (lambda-case
736 (((x y) #f #f #f () (_ _))
c46e0a8a
AW
737 (if (primcall >
738 (lexical y _) (const 0))
de1eb420 739 _ _)))))
c46e0a8a 740 (call (lexical loop _) (toplevel x) (const 0))))
de1eb420
AW
741
742 (pass-if-peval
743 ;; Infinite recursion: `peval' gives up and leaves it as is.
744 (letrec ((f (lambda (x) (g (1- x))))
745 (g (lambda (x) (h (1+ x))))
746 (h (lambda (x) (f x))))
747 (f 0))
748 (letrec _ . _))
749
750 (pass-if-peval
751 ;; Infinite recursion: all the arguments to `loop' are static, but
752 ;; unrolling it would lead `peval' to enter an infinite loop.
753 (let loop ((x 0))
754 (and (< x top)
755 (loop (1+ x))))
756 (letrec (loop) (_) ((lambda . _))
c46e0a8a 757 (call (lexical loop _) (const 0))))
de1eb420
AW
758
759 (pass-if-peval
760 ;; This test checks that the `start' binding is indeed residualized.
761 ;; See the `referenced?' procedure in peval's `prune-bindings'.
762 (let ((pos 0))
de1eb420 763 (let ((here (let ((start pos)) (lambda () start))))
1cd63115 764 (set! pos 1) ;; Cause references to `pos' to residualize.
de1eb420
AW
765 (here)))
766 (let (pos) (_) ((const 0))
1cd63115 767 (let (here) (_) (_)
79d29f96
AW
768 (seq
769 (set! (lexical pos _) (const 1))
770 (call (lexical here _))))))
771
de1eb420
AW
772 (pass-if-peval
773 ;; FIXME: should this one residualize the binding?
774 (letrec ((a a))
775 1)
776 (const 1))
777
778 (pass-if-peval
779 ;; This is a fun one for peval to handle.
780 (letrec ((a a))
781 a)
782 (letrec (a) (_) ((lexical a _))
783 (lexical a _)))
784
785 (pass-if-peval
786 ;; Another interesting recursive case.
787 (letrec ((a b) (b a))
788 a)
789 (letrec (a) (_) ((lexical a _))
790 (lexical a _)))
791
792 (pass-if-peval
793 ;; Another pruning case, that `a' is residualized.
794 (letrec ((a (lambda () (a)))
795 (b (lambda () (a)))
796 (c (lambda (x) x)))
797 (let ((d (foo b)))
798 (c d)))
799
800 ;; "b c a" is the current order that we get with unordered letrec,
801 ;; but it's not important to this test, so if it changes, just adapt
802 ;; the test.
803 (letrec (b c a) (_ _ _)
804 ((lambda _
805 (lambda-case
806 ((() #f #f #f () ())
c46e0a8a 807 (call (lexical a _)))))
de1eb420
AW
808 (lambda _
809 (lambda-case
810 (((x) #f #f #f () (_))
811 (lexical x _))))
812 (lambda _
813 (lambda-case
814 ((() #f #f #f () ())
c46e0a8a 815 (call (lexical a _))))))
de1eb420
AW
816 (let (d)
817 (_)
c46e0a8a
AW
818 ((call (toplevel foo) (lexical b _)))
819 (call (lexical c _) (lexical d _)))))
de1eb420
AW
820
821 (pass-if-peval
822 ;; In this case, we can prune the bindings. `a' ends up being copied
823 ;; because it is only referenced once in the source program. Oh
824 ;; well.
825 (letrec* ((a (lambda (x) (top x)))
826 (b (lambda () a)))
827 (foo (b) (b)))
c46e0a8a
AW
828 (call (toplevel foo)
829 (lambda _
830 (lambda-case
831 (((x) #f #f #f () (_))
832 (call (toplevel top) (lexical x _)))))
833 (lambda _
834 (lambda-case
835 (((x) #f #f #f () (_))
836 (call (toplevel top) (lexical x _)))))))
de1eb420
AW
837
838 (pass-if-peval
839 ;; Constant folding: cons of #nil does not make list
840 (cons 1 #nil)
c46e0a8a 841 (primcall cons (const 1) (const '#nil)))
de1eb420
AW
842
843 (pass-if-peval
844 ;; Constant folding: cons
845 (begin (cons 1 2) #f)
846 (const #f))
847
848 (pass-if-peval
849 ;; Constant folding: cons
850 (begin (cons (foo) 2) #f)
c46e0a8a 851 (seq (call (toplevel foo)) (const #f)))
de1eb420
AW
852
853 (pass-if-peval
854 ;; Constant folding: cons
855 (if (cons 0 0) 1 2)
856 (const 1))
857
858 (pass-if-peval
859 ;; Constant folding: car+cons
860 (car (cons 1 0))
861 (const 1))
862
863 (pass-if-peval
864 ;; Constant folding: cdr+cons
865 (cdr (cons 1 0))
866 (const 0))
867
868 (pass-if-peval
869 ;; Constant folding: car+cons, impure
870 (car (cons 1 (bar)))
c46e0a8a 871 (seq (call (toplevel bar)) (const 1)))
de1eb420
AW
872
873 (pass-if-peval
874 ;; Constant folding: cdr+cons, impure
875 (cdr (cons (bar) 0))
c46e0a8a 876 (seq (call (toplevel bar)) (const 0)))
de1eb420
AW
877
878 (pass-if-peval
879 ;; Constant folding: car+list
880 (car (list 1 0))
881 (const 1))
882
883 (pass-if-peval
884 ;; Constant folding: cdr+list
885 (cdr (list 1 0))
c46e0a8a 886 (primcall list (const 0)))
de1eb420
AW
887
888 (pass-if-peval
889 ;; Constant folding: car+list, impure
890 (car (list 1 (bar)))
c46e0a8a 891 (seq (call (toplevel bar)) (const 1)))
de1eb420
AW
892
893 (pass-if-peval
894 ;; Constant folding: cdr+list, impure
895 (cdr (list (bar) 0))
c46e0a8a
AW
896 (seq (call (toplevel bar)) (primcall list (const 0))))
897
898 (pass-if-peval
899 ;; Equality primitive: same lexical
900 (let ((x (random))) (eq? x x))
901 (seq (call (toplevel random)) (const #t)))
902
903 (pass-if-peval
904 ;; Equality primitive: merge lexical identities
905 (let* ((x (random)) (y x)) (eq? x y))
906 (seq (call (toplevel random)) (const #t)))
de1eb420
AW
907
908 (pass-if-peval
de1eb420
AW
909 ;; Non-constant guards get lexical bindings.
910 (dynamic-wind foo (lambda () bar) baz)
c46e0a8a
AW
911 (let (w u) (_ _) ((toplevel foo) (toplevel baz))
912 (dynwind (lexical w _)
913 (call (lexical w _))
914 (toplevel bar)
915 (call (lexical u _))
916 (lexical u _))))
de1eb420
AW
917
918 (pass-if-peval
de1eb420
AW
919 ;; Constant guards don't need lexical bindings.
920 (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
921 (dynwind
922 (lambda ()
923 (lambda-case
924 ((() #f #f #f () ()) (toplevel foo))))
c46e0a8a 925 (toplevel foo)
de1eb420 926 (toplevel bar)
c46e0a8a 927 (toplevel baz)
de1eb420
AW
928 (lambda ()
929 (lambda-case
930 ((() #f #f #f () ()) (toplevel baz))))))
931
932 (pass-if-peval
de1eb420
AW
933 ;; Prompt is removed if tag is unreferenced
934 (let ((tag (make-prompt-tag)))
935 (call-with-prompt tag
936 (lambda () 1)
937 (lambda args args)))
938 (const 1))
939
940 (pass-if-peval
de1eb420
AW
941 ;; Prompt is removed if tag is unreferenced, with explicit stem
942 (let ((tag (make-prompt-tag "foo")))
943 (call-with-prompt tag
944 (lambda () 1)
945 (lambda args args)))
946 (const 1))
947
948 ;; Handler lambda inlined
949 (pass-if-peval
de1eb420
AW
950 (call-with-prompt tag
951 (lambda () 1)
952 (lambda (k x) x))
953 (prompt (toplevel tag)
954 (const 1)
955 (lambda-case
956 (((k x) #f #f #f () (_ _))
957 (lexical x _)))))
958
959 ;; Handler toplevel not inlined
960 (pass-if-peval
de1eb420
AW
961 (call-with-prompt tag
962 (lambda () 1)
963 handler)
964 (let (handler) (_) ((toplevel handler))
965 (prompt (toplevel tag)
966 (const 1)
967 (lambda-case
968 ((() #f args #f () (_))
c46e0a8a
AW
969 (primcall @apply
970 (lexical handler _)
971 (lexical args _)))))))
de1eb420
AW
972
973 (pass-if-peval
de1eb420
AW
974 ;; `while' without `break' or `continue' has no prompts and gets its
975 ;; condition folded. Unfortunately the outer `lp' does not yet get
976 ;; elided.
977 (while #t #t)
978 (letrec (lp) (_)
979 ((lambda _
980 (lambda-case
981 ((() #f #f #f () ())
982 (letrec (loop) (_)
983 ((lambda _
984 (lambda-case
985 ((() #f #f #f () ())
c46e0a8a
AW
986 (call (lexical loop _))))))
987 (call (lexical loop _)))))))
988 (call (lexical lp _))))
de1eb420
AW
989
990 (pass-if-peval
de1eb420
AW
991 (lambda (a . rest)
992 (apply (lambda (x y) (+ x y))
993 a rest))
994 (lambda _
995 (lambda-case
996 (((x y) #f #f #f () (_ _))
997 _))))
998
c46e0a8a 999 (pass-if-peval
de1eb420 1000 (car '(1 2))
985702f7
AW
1001 (const 1))
1002
1003 ;; If we bail out when inlining an identifier because it's too big,
1004 ;; but the identifier simply aliases some other identifier, then avoid
1005 ;; residualizing a reference to the leaf identifier. The bailout is
1006 ;; driven by the recursive-effort-limit, which is currently 100. We
1007 ;; make sure to trip it with this recursive sum thing.
1008 (pass-if-peval resolve-primitives
1009 (let ((x (let sum ((n 0) (out 0))
1010 (if (< n 10000)
1011 (sum (1+ n) (+ out n))
1012 out))))
1013 ((lambda (y) (list y)) x))
1014 (let (x) (_) (_)
1015 (apply (primitive list) (lexical x _)))))