1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-28 14:00:31 +02:00

Merge remote-tracking branch 'origin/stable-2.0'

This was a pretty big merge involving a fair amount of porting,
especially to peval and its tests.  I did not update psyntax-pp.scm,
that comes in the next commit.

Conflicts:
	module/ice-9/boot-9.scm
	module/ice-9/psyntax-pp.scm
	module/language/ecmascript/compile-tree-il.scm
	module/language/tree-il.scm
	module/language/tree-il/analyze.scm
	module/language/tree-il/inline.scm
	test-suite/tests/tree-il.test
This commit is contained in:
Andy Wingo 2011-09-29 18:02:28 +02:00
commit ca12824581
60 changed files with 3173 additions and 957 deletions

View file

@ -67,6 +67,7 @@ SCM_TESTS = tests/00-initial-env.test \
tests/list.test \
tests/load.test \
tests/match.test \
tests/match.test.upstream \
tests/modules.test \
tests/multilingual.nottest \
tests/net-db.test \

View file

@ -178,6 +178,13 @@ test_scm_take_u8vector_LDADD = $(LIBGUILE_LDADD)
check_PROGRAMS += test-scm-take-u8vector
TESTS += test-scm-take-u8vector
# test-scm-take-u8vector
test_scm_to_latin1_string_SOURCES = test-scm-to-latin1-string.c
test_scm_to_latin1_string_CFLAGS = ${test_cflags}
test_scm_to_latin1_string_LDADD = $(LIBGUILE_LDADD)
check_PROGRAMS += test-scm-to-latin1-string
TESTS += test-scm-to-latin1-string
if HAVE_SHARED_LIBRARIES
# test-extensions

View file

@ -0,0 +1,78 @@
/* Copyright (C) 2011 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <libguile.h>
#include <stdlib.h>
/*
This outputs:
dhansen@localhorst ~/tmp $ ./a.out
foo,bar
bar
*/
#define TEST(x) \
if (!(x)) abort()
static void
inner_main (void *data, int argc, char **argv)
{
char *cstr;
SCM string, tokens, tok;
string = scm_from_latin1_string ("foo,bar");
tokens = scm_string_split (string, SCM_MAKE_CHAR (','));
TEST (scm_is_pair (tokens));
tok = scm_car (tokens);
TEST (scm_is_string (tok));
cstr = scm_to_latin1_string (tok);
TEST (strcmp (cstr, "foo") == 0);
free (cstr);
tokens = scm_cdr (tokens);
TEST (scm_is_pair (tokens));
tok = scm_car (tokens);
TEST (scm_is_string (tok));
cstr = scm_to_latin1_string (tok);
TEST (strcmp (cstr, "bar") == 0);
free (cstr);
tokens = scm_cdr (tokens);
TEST (scm_is_null (tokens));
}
int
main (int argc, char **argv)
{
scm_boot_guile (argc, argv, inner_main, NULL);
return EXIT_SUCCESS;
}
/* Local Variables: */
/* compile-command: "gcc `pkg-config --cflags --libs guile-2.0` main.c" */
/* End: */

View file

@ -1,5 +1,6 @@
;;;; gc.test --- test guile's garbage collection -*- scheme -*-
;;;; Copyright (C) 2000, 2001, 2004, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
;;;; Copyright (C) 2000, 2001, 2004, 2006, 2007, 2008, 2009,
;;;; 2011 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
@ -46,6 +47,13 @@
;;;
;;;
(define (stack-cleanup depth)
;; Clean up stack space for DEPTH words. This is defined here so that
;; `peval' doesn't inline it.
(let cleanup ((i depth))
(and (> i 0)
(begin (cleanup (1- i)) i))))
(with-test-prefix "gc"
(pass-if "after-gc-hook gets called"
@ -65,9 +73,7 @@
(for-each (lambda (x) (guard (make-module))) (iota total))
;; Avoid false references to the modules on the stack.
(let cleanup ((i 20))
(and (> i 0)
(begin (cleanup (1- i)) i)))
(stack-cleanup 20)
(gc)
(gc) ;; twice: have to kill the weak vectors.

View file

@ -1,6 +1,6 @@
;;;; match.test --- (ice-9 match) -*- mode: scheme; coding: utf-8; -*-
;;;;
;;;; Copyright (C) 2010 Free Software Foundation, Inc.
;;;; Copyright (C) 2010, 2011 Free Software Foundation, Inc.
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
@ -18,11 +18,25 @@
(define-module (test-match)
#:use-module (ice-9 match)
#:use-module (srfi srfi-9)
#:use-module (test-suite lib))
(define exception:match-error
(cons 'match-error "^.*$"))
(define-record-type rtd-2-slots
(make-2-slot-record a b)
two-slot-record?
(a slot-first)
(b slot-second))
(define-record-type rtd-3-slots
(make-3-slot-record a b c)
three-slot-record?
(a slot-one)
(b slot-two)
(c slot-three))
(with-test-prefix "matches"
@ -86,7 +100,61 @@
(let ((tree '(one (two 2) (three 3 (and 4 (and 5))))))
(match tree
(('one ('two x) ('three y ('and z '(and 5))))
(equal? (list x y z) '(2 3 4)))))))
(equal? (list x y z) '(2 3 4))))))
(pass-if "and, unique names"
(let ((tree '(1 2)))
(match tree
((and (a 2) (1 b))
(equal? 3 (+ a b))))))
(pass-if "and, same names"
(let ((a '(1 2)))
(match a
((and (a 2) (1 b))
(equal? 3 (+ a b))))))
(with-test-prefix "records"
(pass-if "all slots, bind"
(let ((r (make-3-slot-record 1 2 3)))
(match r
(($ rtd-3-slots a b c)
(equal? (list a b c) '(1 2 3))))))
(pass-if "all slots, literals"
(let ((r (make-3-slot-record 1 2 3)))
(match r
(($ rtd-3-slots 1 2 3)
#t))))
(pass-if "2 slots"
(let ((r (make-3-slot-record 1 2 3)))
(match r
(($ rtd-3-slots x y)
(equal? (list x y) '(1 2))))))
(pass-if "RTD correctly checked"
(let ((r (make-2-slot-record 1 2)))
(match r
(($ rtd-3-slots a b)
#f)
(($ rtd-2-slots a b)
(equal? (list a b) '(1 2))))))
(pass-if "getter"
(match (make-2-slot-record 1 2)
(($ rtd-2-slots (get! first) (get! second))
(equal? (list (first) (second)) '(1 2)))))
(pass-if "setter"
(let ((r (make-2-slot-record 1 2)))
(match r
(($ rtd-2-slots (set! set-first!) (set! set-second!))
(set-first! 'one)
(set-second! 'two)
(equal? (list (slot-first r) (slot-second r))
'(one two))))))))
(with-test-prefix "doesn't match"
@ -105,4 +173,36 @@
exception:match-error
(match '(a 0)
(((and x (? symbol?)) ..1)
(equal? x '(a b c))))))
(equal? x '(a b c)))))
(with-test-prefix "records"
(pass-if "not a record"
(match "hello"
(($ rtd-2-slots) #f)
(_ #t)))
(pass-if-exception "too many slots"
exception:out-of-range
(let ((r (make-3-slot-record 1 2 3)))
(match r
(($ rtd-3-slots a b c d)
#f))))))
;;;
;;; Upstream tests, from Chibi-Scheme (3-clause BSD license).
;;;
(let-syntax ((load (syntax-rules ()
((_ file) #t)))
(test (syntax-rules ()
((_ name expected expr)
(pass-if name
(equal? expected expr)))))
(test-begin (syntax-rules ()
((_ name) #t)))
(test-end (syntax-rules ()
((_) #t))))
(with-test-prefix "upstream tests"
(include-from-path "test-suite/tests/match.test.upstream")))

View file

@ -0,0 +1,168 @@
(cond-expand
(modules (import (chibi match) (only (chibi test) test-begin test test-end)))
(else (load "lib/chibi/match/match.scm")))
(test-begin "match")
(test "any" 'ok (match 'any (_ 'ok)))
(test "symbol" 'ok (match 'ok (x x)))
(test "number" 'ok (match 28 (28 'ok)))
(test "string" 'ok (match "good" ("bad" 'fail) ("good" 'ok)))
(test "literal symbol" 'ok (match 'good ('bad 'fail) ('good 'ok)))
(test "null" 'ok (match '() (() 'ok)))
(test "pair" 'ok (match '(ok) ((x) x)))
(test "vector" 'ok (match '#(ok) (#(x) x)))
(test "any doubled" 'ok (match '(1 2) ((_ _) 'ok)))
(test "and empty" 'ok (match '(o k) ((and) 'ok)))
(test "and single" 'ok (match 'ok ((and x) x)))
(test "and double" 'ok (match 'ok ((and (? symbol?) y) 'ok)))
(test "or empty" 'ok (match '(o k) ((or) 'fail) (else 'ok)))
(test "or single" 'ok (match 'ok ((or x) 'ok)))
(test "or double" 'ok (match 'ok ((or (? symbol? y) y) y)))
(test "not" 'ok (match 28 ((not (a . b)) 'ok)))
(test "pred" 'ok (match 28 ((? number?) 'ok)))
(test "named pred" 29 (match 28 ((? number? x) (+ x 1))))
(test "duplicate symbols pass" 'ok (match '(ok . ok) ((x . x) x)))
(test "duplicate symbols fail" 'ok (match '(ok . bad) ((x . x) 'bad) (else 'ok)))
(test "duplicate symbols samth" 'ok (match '(ok . ok) ((x . 'bad) x) (('ok . x) x)))
(test "ellipses" '((a b c) (1 2 3))
(match '((a . 1) (b . 2) (c . 3))
(((x . y) ___) (list x y))))
(test "real ellipses" '((a b c) (1 2 3))
(match '((a . 1) (b . 2) (c . 3))
(((x . y) ...) (list x y))))
(test "vector ellipses" '(1 2 3 (a b c) (1 2 3))
(match '#(1 2 3 (a . 1) (b . 2) (c . 3))
(#(a b c (hd . tl) ...) (list a b c hd tl))))
(test "pred ellipses" '(1 2 3)
(match '(1 2 3)
(((? odd? n) ___) n)
(((? number? n) ___) n)))
(test "failure continuation" 'ok
(match '(1 2)
((a . b) (=> next) (if (even? a) 'fail (next)))
((a . b) 'ok)))
(test "let" '(o k)
(match-let ((x 'ok) (y '(o k))) y))
(test "let*" '(f o o f)
(match-let* ((x 'f) (y 'o) ((z w) (list y x))) (list x y z w)))
(test "getter car" '(1 2)
(match '(1 . 2) (((get! a) . b) (list (a) b))))
(test "getter cdr" '(1 2)
(match '(1 . 2) ((a . (get! b)) (list a (b)))))
(test "getter vector" '(1 2 3)
(match '#(1 2 3) (#((get! a) b c) (list (a) b c))))
(test "setter car" '(3 . 2)
(let ((x (cons 1 2)))
(match x (((set! a) . b) (a 3)))
x))
(test "setter cdr" '(1 . 3)
(let ((x (cons 1 2)))
(match x ((a . (set! b)) (b 3)))
x))
(test "setter vector" '#(1 0 3)
(let ((x (vector 1 2 3)))
(match x (#(a (set! b) c) (b 0)))
x))
(test "single tail" '((a b) (1 2) (c . 3))
(match '((a . 1) (b . 2) (c . 3))
(((x . y) ... last) (list x y last))))
(test "single tail 2" '((a b) (1 2) 3)
(match '((a . 1) (b . 2) 3)
(((x . y) ... last) (list x y last))))
(test "multiple tail" '((a b) (1 2) (c . 3) (d . 4) (e . 5))
(match '((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))
(((x . y) ... u v w) (list x y u v w))))
(test "tail against improper list" #f
(match '(a b c d e f . g)
((x ... y u v w) (list x y u v w))
(else #f)))
(test "Riastradh quasiquote" '(2 3)
(match '(1 2 3) (`(1 ,b ,c) (list b c))))
(test "trivial tree search" '(1 2 3)
(match '(1 2 3) ((_ *** (a b c)) (list a b c))))
(test "simple tree search" '(1 2 3)
(match '(x (1 2 3)) ((_ *** (a b c)) (list a b c))))
(test "deep tree search" '(1 2 3)
(match '(x (x (x (1 2 3)))) ((_ *** (a b c)) (list a b c))))
(test "non-tail tree search" '(1 2 3)
(match '(x (x (x a b c (1 2 3) d e f))) ((_ *** (a b c)) (list a b c))))
(test "restricted tree search" '(1 2 3)
(match '(x (x (x a b c (1 2 3) d e f))) (('x *** (a b c)) (list a b c))))
(test "fail restricted tree search" #f
(match '(x (y (x a b c (1 2 3) d e f)))
(('x *** (a b c)) (list a b c))
(else #f)))
(test "sxml tree search" '(((href . "http://synthcode.com/")) ("synthcode"))
(match '(p (ul (li a (b c) (a (^ (href . "http://synthcode.com/")) "synthcode") d e f)))
(((or 'p 'ul 'li 'b) *** ('a ('^ attrs ...) text ...))
(list attrs text))
(else #f)))
(test "failed sxml tree search" #f
(match '(p (ol (li a (b c) (a (^ (href . "http://synthcode.com/")) "synthcode") d e f)))
(((or 'p 'ul 'li 'b) *** ('a ('^ attrs ...) text ...))
(list attrs text))
(else #f)))
(test "collect tree search"
'((p ul li) ((href . "http://synthcode.com/")) ("synthcode"))
(match '(p (ul (li a (b c) (a (^ (href . "http://synthcode.com/")) "synthcode") d e f)))
(((and tag (or 'p 'ul 'li 'b)) *** ('a ('^ attrs ...) text ...))
(list tag attrs text))
(else #f)))
(test "anded tail pattern" '(1 2)
(match '(1 2 3) ((and (a ... b) x) a)))
(test "anded search pattern" '(a b c)
(match '(a (b (c d))) ((and (p *** 'd) x) p)))
(test "joined tail" '(1 2)
(match '(1 2 3) ((and (a ... b) x) a)))
(test "list ..1" '(a b c)
(match '(a b c) ((x ..1) x)))
(test "list ..1 failed" #f
(match '()
((x ..1) x)
(else #f)))
(test "list ..1 with predicate" '(a b c)
(match '(a b c)
(((and x (? symbol?)) ..1) x)))
(test "list ..1 with failed predicate" #f
(match '(a b 3)
(((and x (? symbol?)) ..1) x)
(else #f)))
(test-end)

View file

@ -40,8 +40,11 @@
;; make sure these are compiled so we're not swamped in `eval'
(define (make-func)
;; Disable partial evaluation so that `(+ i i)' doesn't get
;; stripped.
(compile '(lambda (n)
(do ((i 0 (+ i 1))) ((= 200 i)) (+ i i)))))
(do ((i 0 (+ i 1))) ((= 200 i)) (+ i i)))
#:opts '(#:partial-eval? #f)))
(define run-test
(compile '(lambda (num-calls funcs)
(let loop ((x num-calls) (funcs funcs))
@ -50,11 +53,11 @@
((car funcs) x)
(loop (- x 1) (cdr funcs))))))))
(let ((num-calls 40000)
(let ((num-calls 80000)
(funcs (circular-list (make-func) (make-func) (make-func))))
;; Run test. 10000 us == 100 Hz.
(statprof-reset 0 10000 #f #f)
;; Run test. 20000 us == 200 Hz.
(statprof-reset 0 20000 #f #f)
(statprof-start)
(run-test num-calls funcs)
(statprof-stop)

View file

@ -36,6 +36,13 @@
(equal? '(a b c) '(a b c))
a))
(define (stack-cleanup depth)
;; Clean up stack space for DEPTH words. This is defined here so that
;; `peval' doesn't inline it.
(let cleanup ((i depth))
(and (> i 0)
(begin (cleanup (1- i)) i))))
(if (provided? 'threads)
(begin
@ -403,9 +410,7 @@
(g (let ((m (make-mutex))) (lock-mutex m) m))
;; Avoid false references to M on the stack.
(let cleanup ((i 20))
(and (> i 0)
(begin (cleanup (1- i)) i)))
(stack-cleanup 20)
(gc) (gc)
(let ((m (g)))

View file

@ -23,6 +23,7 @@
#:use-module (system base pmatch)
#:use-module (system base message)
#:use-module (language tree-il)
#:use-module (language tree-il primitives)
#:use-module (language glil)
#:use-module (srfi srfi-13))
@ -34,26 +35,28 @@
(post-order! (lambda (x) (set! (tree-il-src x) #f))
x))
(define-syntax assert-scheme->glil
(syntax-rules ()
((_ in out)
(let ((tree-il (strip-source
(compile 'in #:from 'scheme #:to 'tree-il))))
(pass-if 'in
(equal? (unparse-glil (compile tree-il #:from 'tree-il #:to 'glil))
'out))))))
(define-syntax assert-tree-il->glil
(syntax-rules ()
((_ in pat test ...)
(syntax-rules (with-partial-evaluation without-partial-evaluation
with-options)
((_ with-partial-evaluation in pat test ...)
(assert-tree-il->glil with-options (#:partial-eval? #t)
in pat test ...))
((_ without-partial-evaluation in pat test ...)
(assert-tree-il->glil with-options (#:partial-eval? #f)
in pat test ...))
((_ with-options opts in pat test ...)
(let ((exp 'in))
(pass-if 'in
(let ((glil (unparse-glil
(compile (strip-source (parse-tree-il exp))
#:from 'tree-il #:to 'glil))))
#:from 'tree-il #:to 'glil
#:opts 'opts))))
(pmatch glil
(pat (guard test ...) #t)
(else #f))))))))
(else #f))))))
((_ in pat test ...)
(assert-tree-il->glil with-partial-evaluation
in pat test ...))))
(define-syntax pass-if-tree-il->scheme
(syntax-rules ()
@ -66,6 +69,39 @@
(pat (guard guard-exp) #t)
(_ #f))))))
(define peval
;; The partial evaluator.
(@@ (language tree-il optimize) peval))
(define-syntax pass-if-peval
(syntax-rules (resolve-primitives)
((_ in pat)
(pass-if-peval in pat
(compile 'in #:from 'scheme #:to 'tree-il)))
((_ resolve-primitives in pat)
(pass-if-peval in pat
(expand-primitives!
(resolve-primitives!
(compile 'in #:from 'scheme #:to 'tree-il)
(current-module)))))
((_ in pat code)
(pass-if 'in
(let ((evaled (unparse-tree-il (peval code))))
(pmatch evaled
(pat #t)
(_ (pk 'peval-mismatch)
((@ (ice-9 pretty-print) pretty-print)
'in)
(newline)
((@ (ice-9 pretty-print) pretty-print)
evaled)
(newline)
((@ (ice-9 pretty-print) pretty-print)
'pat)
(newline)
#f)))))))
(with-test-prefix "tree-il->scheme"
(pass-if-tree-il->scheme
(case-lambda ((a) a) ((b c) (list b c)))
@ -107,8 +143,8 @@
(const 1) (call return 1)
(label ,l2) (const 2) (call return 1))
(eq? l1 l2))
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(begin (if (toplevel foo) (const 1) (const 2)) (const #f))
(program () (std-prelude 0 0 #f) (label _) (toplevel ref foo) (branch br-if-not ,l1) (branch br ,l2)
(label ,l3) (label ,l4) (const #f) (call return 1))
@ -137,21 +173,21 @@
(call return 1))))
(with-test-prefix "lexical refs"
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(let (x) (y) ((const 1)) (lexical x y))
(program () (std-prelude 0 1 #f) (label _)
(const 1) (bind (x #f 0)) (lexical #t #f set 0)
(lexical #t #f ref 0) (call return 1)
(unbind)))
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(let (x) (y) ((const 1)) (begin (lexical x y) (const #f)))
(program () (std-prelude 0 1 #f) (label _)
(const 1) (bind (x #f 0)) (lexical #t #f set 0)
(const #f) (call return 1)
(unbind)))
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(let (x) (y) ((const 1)) (primcall null? (lexical x y)))
(program () (std-prelude 0 1 #f) (label _)
(const 1) (bind (x #f 0)) (lexical #t #f set 0)
@ -270,7 +306,7 @@
(toplevel ref bar)
(call return 1)))
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(begin (toplevel bar) (const #f))
(program () (std-prelude 0 0 #f) (label _)
(toplevel ref bar) (call drop 1)
@ -332,13 +368,14 @@
(const #f) (call return 1)))
(assert-tree-il->glil
;; This gets simplified by `peval'.
(primcall null? (const 2))
(program () (std-prelude 0 0 #f) (label _)
(const 2) (call null? 1) (call return 1))))
(const #f) (call return 1))))
(with-test-prefix "letrec"
;; simple bindings -> let
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(letrec (x y) (x1 y1) ((const 10) (const 20))
(call (toplevel foo) (lexical x x1) (lexical y y1)))
(program () (std-prelude 0 2 #f) (label _)
@ -351,7 +388,7 @@
(unbind)))
;; complex bindings -> box and set! within let
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(letrec (x y) (x1 y1) ((call (toplevel foo)) (call (toplevel bar)))
(primcall + (lexical x x1) (lexical y y1)))
(program () (std-prelude 0 4 #f) (label _)
@ -367,7 +404,7 @@
(call add 2) (call return 1) (unbind)))
;; complex bindings in letrec* -> box and set! in order
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(letrec* (x y) (x1 y1) ((call (toplevel foo)) (call (toplevel bar)))
(primcall + (lexical x x1) (lexical y y1)))
(program () (std-prelude 0 2 #f) (label _)
@ -383,7 +420,7 @@
(call add 2) (call return 1) (unbind)))
;; simple bindings in letrec* -> equivalent to letrec
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(letrec* (x y) (xx yy) ((const 1) (const 2))
(lexical y yy))
(program () (std-prelude 0 1 #f) (label _)
@ -487,9 +524,10 @@
(const #t) (call return 1)))
(assert-tree-il->glil
;; This gets simplified by `peval'.
(primcall null? (begin (const #f) (const 2)))
(program () (std-prelude 0 0 #f) (label _)
(const 2) (call null? 1) (call return 1))))
(const #f) (call return 1))))
(with-test-prefix "values"
(assert-tree-il->glil
@ -514,7 +552,7 @@
;; FIXME: binding info for or-hacked locals might bork the disassembler,
;; and could be tightened in any case
(with-test-prefix "the or hack"
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(let (x) (y) ((const 1))
(if (lexical x y)
(lexical x y)
@ -532,7 +570,7 @@
(eq? l1 l2))
;; second bound var is unreferenced
(assert-tree-il->glil
(assert-tree-il->glil without-partial-evaluation
(let (x) (y) ((const 1))
(if (lexical x y)
(lexical x y)
@ -586,6 +624,693 @@
(toplevel ref bar) (call call/cc 1)
(call tail-call 1))))
(with-test-prefix "partial evaluation"
(pass-if-peval
;; First order, primitive.
(let ((x 1) (y 2)) (+ x y))
(const 3))
(pass-if-peval
;; First order, thunk.
(let ((x 1) (y 2))
(let ((f (lambda () (+ x y))))
(f)))
(const 3))
(pass-if-peval resolve-primitives
;; First order, let-values (requires primitive expansion for
;; `call-with-values'.)
(let ((x 0))
(call-with-values
(lambda () (if (zero? x) (values 1 2) (values 3 4)))
(lambda (a b)
(+ a b))))
(const 3))
(pass-if-peval
;; First order, coalesced, mutability preserved.
(cons 0 (cons 1 (cons 2 (list 3 4 5))))
(primcall list
(const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
(pass-if-peval
;; First order, coalesced, mutability preserved.
(cons 0 (cons 1 (cons 2 (list 3 4 5))))
;; This must not be a constant.
(primcall list
(const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
(pass-if-peval
;; First order, coalesced, immutability preserved.
(cons 0 (cons 1 (cons 2 '(3 4 5))))
(primcall cons (const 0)
(primcall cons (const 1)
(primcall cons (const 2)
(const (3 4 5))))))
;; These two tests doesn't work any more because we changed the way we
;; deal with constants -- now the algorithm will see a construction as
;; being bound to the lexical, so it won't propagate it. It can't
;; even propagate it in the case that it is only referenced once,
;; because:
;;
;; (let ((x (cons 1 2))) (lambda () x))
;;
;; is not the same as
;;
;; (lambda () (cons 1 2))
;;
;; Perhaps if we determined that not only was it only referenced once,
;; it was not closed over by a lambda, then we could propagate it, and
;; re-enable these two tests.
;;
#;
(pass-if-peval
;; First order, mutability preserved.
(let loop ((i 3) (r '()))
(if (zero? i)
r
(loop (1- i) (cons (cons i i) r))))
(primcall list
(primcall cons (const 1) (const 1))
(primcall cons (const 2) (const 2))
(primcall cons (const 3) (const 3))))
;;
;; See above.
#;
(pass-if-peval
;; First order, evaluated.
(let loop ((i 7)
(r '()))
(if (<= i 0)
(car r)
(loop (1- i) (cons i r))))
(const 1))
;; Instead here are tests for what happens for the above cases: they
;; unroll but they don't fold.
(pass-if-peval
(let loop ((i 3) (r '()))
(if (zero? i)
r
(loop (1- i) (cons (cons i i) r))))
(letrec (loop) (_) (_)
(let (r) (_)
((primcall list
(primcall cons (const 3) (const 3))))
(let (r) (_)
((primcall cons
(primcall cons (const 2) (const 2))
(lexical r _)))
(primcall cons
(primcall cons (const 1) (const 1))
(lexical r _))))))
;; See above.
(pass-if-peval
(let loop ((i 4)
(r '()))
(if (<= i 0)
(car r)
(loop (1- i) (cons i r))))
(letrec (loop) (_) (_)
(let (r) (_)
((primcall list (const 4)))
(let (r) (_)
((primcall cons
(const 3)
(lexical r _)))
(let (r) (_)
((primcall cons
(const 2)
(lexical r _)))
(let (r) (_)
((primcall cons
(const 1)
(lexical r _)))
(primcall car
(lexical r _))))))))
;; Static sums.
(pass-if-peval
(let loop ((l '(1 2 3 4)) (sum 0))
(if (null? l)
sum
(loop (cdr l) (+ sum (car l)))))
(const 10))
(pass-if-peval
;; Mutability preserved.
((lambda (x y z) (list x y z)) 1 2 3)
(primcall list (const 1) (const 2) (const 3)))
(pass-if-peval
;; Don't propagate effect-free expressions that operate on mutable
;; objects.
(let* ((x (list 1))
(y (car x)))
(set-car! x 0)
y)
(let (x) (_) ((primcall list (const 1)))
(let (y) (_) ((primcall car (lexical x _)))
(seq
(call (toplevel set-car!) (lexical x _) (const 0))
(lexical y _)))))
(pass-if-peval
;; Don't propagate effect-free expressions that operate on objects we
;; don't know about.
(let ((y (car x)))
(set-car! x 0)
y)
(let (y) (_) ((primcall car (toplevel x)))
(seq
(call (toplevel set-car!) (toplevel x) (const 0))
(lexical y _))))
(pass-if-peval
;; Infinite recursion
((lambda (x) (x x)) (lambda (x) (x x)))
(let (x) (_)
((lambda _
(lambda-case
(((x) _ _ _ _ _)
(call (lexical x _) (lexical x _))))))
(call (lexical x _) (lexical x _))))
(pass-if-peval
;; First order, aliased primitive.
(let* ((x *) (y (x 1 2))) y)
(const 2))
(pass-if-peval
;; First order, shadowed primitive.
(begin
(define (+ x y) (pk x y))
(+ 1 2))
(seq
(define +
(lambda (_)
(lambda-case
(((x y) #f #f #f () (_ _))
(call (toplevel pk) (lexical x _) (lexical y _))))))
(call (toplevel +) (const 1) (const 2))))
(pass-if-peval
;; First-order, effects preserved.
(let ((x 2))
(do-something!)
x)
(seq
(call (toplevel do-something!))
(const 2)))
(pass-if-peval
;; First order, residual bindings removed.
(let ((x 2) (y 3))
(* (+ x y) z))
(primcall * (const 5) (toplevel z)))
(pass-if-peval
;; First order, with lambda.
(define (foo x)
(define (bar z) (* z z))
(+ x (bar 3)))
(define foo
(lambda (_)
(lambda-case
(((x) #f #f #f () (_))
(primcall + (lexical x _) (const 9)))))))
(pass-if-peval
;; First order, with lambda inlined & specialized twice.
(let ((f (lambda (x y)
(+ (* x top) y)))
(x 2)
(y 3))
(+ (* x (f x y))
(f something x)))
(primcall +
(primcall *
(const 2)
(primcall + ; (f 2 3)
(primcall *
(const 2)
(toplevel top))
(const 3)))
(let (x) (_) ((toplevel something)) ; (f something 2)
;; `something' is not const, so preserve order of
;; effects with a lexical binding.
(primcall +
(primcall *
(lexical x _)
(toplevel top))
(const 2)))))
(pass-if-peval
;; First order, with lambda inlined & specialized 3 times.
(let ((f (lambda (x y) (if (> x 0) y x))))
(+ (f -1 0)
(f 1 0)
(f -1 y)
(f 2 y)
(f z y)))
(primcall +
(const -1) ; (f -1 0)
(const 0) ; (f 1 0)
(seq (toplevel y) (const -1)) ; (f -1 y)
(toplevel y) ; (f 2 y)
(let (x y) (_ _) ((toplevel z) (toplevel y)) ; (f z y)
(if (primcall > (lexical x _) (const 0))
(lexical y _)
(lexical x _)))))
(pass-if-peval
;; First order, conditional.
(let ((y 2))
(lambda (x)
(if (> y 0)
(display x)
'never-reached)))
(lambda ()
(lambda-case
(((x) #f #f #f () (_))
(call (toplevel display) (lexical x _))))))
(pass-if-peval
;; First order, recursive procedure.
(letrec ((fibo (lambda (n)
(if (<= n 1)
n
(+ (fibo (- n 1))
(fibo (- n 2)))))))
(fibo 4))
(const 3))
(pass-if-peval
;; Don't propagate toplevel references, as intervening expressions
;; could alter their bindings.
(let ((x top))
(foo)
x)
(let (x) (_) ((toplevel top))
(seq
(call (toplevel foo))
(lexical x _))))
(pass-if-peval
;; Higher order.
((lambda (f x)
(f (* (car x) (cadr x))))
(lambda (x)
(+ x 1))
'(2 3))
(const 7))
(pass-if-peval
;; Higher order with optional argument (default value).
((lambda* (f x #:optional (y 0))
(+ y (f (* (car x) (cadr x)))))
(lambda (x)
(+ x 1))
'(2 3))
(const 7))
(pass-if-peval
;; Higher order with optional argument (caller-supplied value).
((lambda* (f x #:optional (y 0))
(+ y (f (* (car x) (cadr x)))))
(lambda (x)
(+ x 1))
'(2 3)
35)
(const 42))
(pass-if-peval
;; Higher order with optional argument (side-effecting default
;; value).
((lambda* (f x #:optional (y (foo)))
(+ y (f (* (car x) (cadr x)))))
(lambda (x)
(+ x 1))
'(2 3))
(let (y) (_) ((call (toplevel foo)))
(primcall + (lexical y _) (const 7))))
(pass-if-peval
;; Higher order with optional argument (caller-supplied value).
((lambda* (f x #:optional (y (foo)))
(+ y (f (* (car x) (cadr x)))))
(lambda (x)
(+ x 1))
'(2 3)
35)
(const 42))
(pass-if-peval
;; Higher order.
((lambda (f) (f x)) (lambda (x) x))
(toplevel x))
(pass-if-peval
;; Bug reported at
;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html>.
(let ((fold (lambda (f g) (f (g top)))))
(fold 1+ (lambda (x) x)))
(primcall 1+ (toplevel top)))
(pass-if-peval
;; Procedure not inlined when residual code contains recursive calls.
;; <http://debbugs.gnu.org/9542>
(letrec ((fold (lambda (f x3 b null? car cdr)
(if (null? x3)
b
(f (car x3) (fold f (cdr x3) b null? car cdr))))))
(fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
(letrec (fold) (_) (_)
(call (lexical fold _)
(primitive *)
(toplevel x)
(const 1)
(primitive zero?)
(lambda ()
(lambda-case
(((x1) #f #f #f () (_))
(lexical x1 _))))
(lambda ()
(lambda-case
(((x2) #f #f #f () (_))
(primcall - (lexical x2 _) (const 1))))))))
(pass-if "inlined lambdas are alpha-renamed"
;; In this example, `make-adder' is inlined more than once; thus,
;; they should use different gensyms for their arguments, because
;; the various optimization passes assume uniquely-named variables.
;;
;; Bug reported at
;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
(pmatch (unparse-tree-il
(peval (compile
'(let ((make-adder
(lambda (x) (lambda (y) (+ x y)))))
(cons (make-adder 1) (make-adder 2)))
#:to 'tree-il)))
((primcall cons
(lambda ()
(lambda-case
(((y) #f #f #f () (,gensym1))
(primcall +
(const 1)
(lexical y ,ref1)))))
(lambda ()
(lambda-case
(((y) #f #f #f () (,gensym2))
(primcall +
(const 2)
(lexical y ,ref2))))))
(and (eq? gensym1 ref1)
(eq? gensym2 ref2)
(not (eq? gensym1 gensym2))))
(_ #f)))
(pass-if-peval
;; Higher order, mutually recursive procedures.
(letrec ((even? (lambda (x)
(or (= 0 x)
(odd? (- x 1)))))
(odd? (lambda (x)
(not (even? (- x 1))))))
(and (even? 4) (odd? 7)))
(const #t))
;;
;; Below are cases where constant propagation should bail out.
;;
(pass-if-peval
;; Non-constant lexical is not propagated.
(let ((v (make-vector 6 #f)))
(lambda (n)
(vector-set! v n n)))
(let (v) (_)
((call (toplevel make-vector) (const 6) (const #f)))
(lambda ()
(lambda-case
(((n) #f #f #f () (_))
(call (toplevel vector-set!)
(lexical v _) (lexical n _) (lexical n _)))))))
(pass-if-peval
;; Mutable lexical is not propagated.
(let ((v (vector 1 2 3)))
(lambda ()
v))
(let (v) (_)
((primcall vector (const 1) (const 2) (const 3)))
(lambda ()
(lambda-case
((() #f #f #f () ())
(lexical v _))))))
(pass-if-peval
;; Lexical that is not provably pure is not inlined nor propagated.
(let* ((x (if (> p q) (frob!) (display 'chbouib)))
(y (* x 2)))
(+ x x y))
(let (x) (_) ((if (primcall > (toplevel p) (toplevel q))
(call (toplevel frob!))
(call (toplevel display) (const chbouib))))
(let (y) (_) ((primcall * (lexical x _) (const 2)))
(primcall +
(lexical x _) (lexical x _) (lexical y _)))))
(pass-if-peval
;; Non-constant arguments not propagated to lambdas.
((lambda (x y z)
(vector-set! x 0 0)
(set-car! y 0)
(set-cdr! z '()))
(vector 1 2 3)
(make-list 10)
(list 1 2 3))
(let (x y z) (_ _ _)
((primcall vector (const 1) (const 2) (const 3))
(call (toplevel make-list) (const 10))
(primcall list (const 1) (const 2) (const 3)))
(seq
(call (toplevel vector-set!)
(lexical x _) (const 0) (const 0))
(seq (call (toplevel set-car!)
(lexical y _) (const 0))
(call (toplevel set-cdr!)
(lexical z _) (const ()))))))
(pass-if-peval
(let ((foo top-foo) (bar top-bar))
(let* ((g (lambda (x y) (+ x y)))
(f (lambda (g x) (g x x))))
(+ (f g foo) (f g bar))))
(let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
(primcall +
(primcall + (lexical foo _) (lexical foo _))
(primcall + (lexical bar _) (lexical bar _)))))
(pass-if-peval
;; Fresh objects are not turned into constants, nor are constants
;; turned into fresh objects.
(let* ((c '(2 3))
(x (cons 1 c))
(y (cons 0 x)))
y)
(let (x) (_) ((primcall cons (const 1) (const (2 3))))
(primcall cons (const 0) (lexical x _))))
(pass-if-peval
;; Bindings mutated.
(let ((x 2))
(set! x 3)
x)
(let (x) (_) ((const 2))
(seq
(set! (lexical x _) (const 3))
(lexical x _))))
(pass-if-peval
;; Bindings mutated.
(letrec ((x 0)
(f (lambda ()
(set! x (+ 1 x))
x)))
(frob f) ; may mutate `x'
x)
(letrec (x) (_) ((const 0))
(seq
(call (toplevel frob) (lambda _ _))
(lexical x _))))
(pass-if-peval
;; Bindings mutated.
(letrec ((f (lambda (x)
(set! f (lambda (_) x))
x)))
(f 2))
(letrec _ . _))
(pass-if-peval
;; Bindings possibly mutated.
(let ((x (make-foo)))
(frob! x) ; may mutate `x'
x)
(let (x) (_) ((call (toplevel make-foo)))
(seq
(call (toplevel frob!) (lexical x _))
(lexical x _))))
(pass-if-peval
;; Inlining stops at recursive calls with dynamic arguments.
(let loop ((x x))
(if (< x 0) x (loop (1- x))))
(letrec (loop) (_) ((lambda (_)
(lambda-case
(((x) #f #f #f () (_))
(if _ _
(call (lexical loop _)
(primcall 1-
(lexical x _))))))))
(call (lexical loop _) (toplevel x))))
(pass-if-peval
;; Recursion on the 2nd argument is fully evaluated.
(let ((x (top)))
(let loop ((x x) (y 10))
(if (> y 0)
(loop x (1- y))
(foo x y))))
(let (x) (_) ((call (toplevel top)))
(letrec (loop) (_) (_)
(call (toplevel foo) (lexical x _) (const 0)))))
(pass-if-peval
;; Inlining aborted when residual code contains recursive calls.
;;
;; <http://debbugs.gnu.org/9542>
(let loop ((x x) (y 0))
(if (> y 0)
(loop (1- x) (1- y))
(if (< x 0)
x
(loop (1+ x) (1+ y)))))
(letrec (loop) (_) ((lambda (_)
(lambda-case
(((x y) #f #f #f () (_ _))
(if (primcall >
(lexical y _) (const 0))
_ _)))))
(call (lexical loop _) (toplevel x) (const 0))))
(pass-if-peval
;; Infinite recursion: `peval' gives up and leaves it as is.
(letrec ((f (lambda (x) (g (1- x))))
(g (lambda (x) (h (1+ x))))
(h (lambda (x) (f x))))
(f 0))
(letrec _ . _))
(pass-if-peval
;; Constant folding: cons
(begin (cons 1 2) #f)
(const #f))
(pass-if-peval
;; Constant folding: cons
(begin (cons (foo) 2) #f)
(seq (call (toplevel foo)) (const #f)))
(pass-if-peval
;; Constant folding: cons
(if (cons 0 0) 1 2)
(const 1))
(pass-if-peval
;; Constant folding: car+cons
(car (cons 1 0))
(const 1))
(pass-if-peval
;; Constant folding: cdr+cons
(cdr (cons 1 0))
(const 0))
(pass-if-peval
;; Constant folding: car+cons, impure
(car (cons 1 (bar)))
(seq (call (toplevel bar)) (const 1)))
(pass-if-peval
;; Constant folding: cdr+cons, impure
(cdr (cons (bar) 0))
(seq (call (toplevel bar)) (const 0)))
(pass-if-peval
;; Constant folding: car+list
(car (list 1 0))
(const 1))
(pass-if-peval
;; Constant folding: cdr+list
(cdr (list 1 0))
(primcall list (const 0)))
(pass-if-peval
;; Constant folding: car+list, impure
(car (list 1 (bar)))
(seq (call (toplevel bar)) (const 1)))
(pass-if-peval
;; Constant folding: cdr+list, impure
(cdr (list (bar) 0))
(seq (call (toplevel bar)) (primcall list (const 0))))
(pass-if-peval
resolve-primitives
;; Prompt is removed if tag is unreferenced
(let ((tag (make-prompt-tag)))
(call-with-prompt tag
(lambda () 1)
(lambda args args)))
(const 1))
(pass-if-peval
resolve-primitives
;; Prompt is removed if tag is unreferenced, with explicit stem
(let ((tag (make-prompt-tag "foo")))
(call-with-prompt tag
(lambda () 1)
(lambda args args)))
(const 1))
(pass-if-peval
resolve-primitives
;; `while' without `break' or `continue' has no prompts and gets its
;; condition folded. Unfortunately the outer `lp' does not yet get
;; elided.
(while #t #t)
(letrec (lp) (_)
((lambda _
(lambda-case
((() #f #f #f () ())
(letrec (loop) (_)
((lambda _
(lambda-case
((() #f #f #f () ())
(call (lexical loop _))))))
(call (lexical loop _)))))))
(call (lexical lp _)))))
(with-test-prefix "tree-il-fold"

View file

@ -89,6 +89,9 @@
(pass-if-parse date "Tue, 15 Nov 1994 08:12:31 GMT"
(string->date "Tue, 15 Nov 1994 08:12:31 +0000"
"~a, ~d ~b ~Y ~H:~M:~S ~z"))
(pass-if-parse date "Wed, 7 Sep 2011 11:25:00 GMT"
(string->date "Wed, 7 Sep 2011 11:25:00 +0000"
"~a,~e ~b ~Y ~H:~M:~S ~z"))
(pass-if-parse-error date "Tue, 15 Nov 1994 08:12:31 EST" date)
(pass-if-any-error date "Tue, 15 Qux 1994 08:12:31 EST")

View file

@ -12,7 +12,7 @@
(define (matches? obj)
; (format #t "matches? ~a~%" obj)
(match obj
(($ stuff) #t)
(($ <stuff>) #t)
; (blurps #t)
("hello" #t)
(else #f)))