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