x86_64ではchibi-schemeが動作しない?

Makefileに細工してコンパイルできるようになったchibi-schemeですが、テストが付いてきているので実行して見たところ、途中で止まってしまい先に進みません。

$ make test
((lambda (x) (+ x x)) 4) ................................................ [PASS]
((lambda x x) 3 4 5 6) .................................................. [PASS]
((lambda (x y . z) z) 3 4 5 6) .......................................... [PASS]
(if (> 3 2) (quote yes) (quote no)) ..................................... [PASS]
(if (> 2 3) (quote yes) (quote no)) ..................................... [PASS]
(if (> 3 2) (- 3 2) (+ 3 2)) ............................................ [PASS]
(cond ((> 3 2) (quote greater)) ((< 3 2) (quote less))) ................. [PASS]
(cond ((> 3 3) (quote greater)) ((< 3 3) (quote less)) (else (quote equal)))  [PASS]
(case (* 2 3) ((2 3 5 7) (quote prime)) ((1 4 6 8 9) (quote composite)))  [PASS]
(case (car (quote (c d))) ((a e i o u) (quote vowel)) ((w y) (quote semivowel)) (else (quote consonant)))  [PASS]
(and (= 2 2) (> 2 1)) ................................................... [PASS]
(and (= 2 2) (< 2 1)) ................................................... [PASS]
(and 1 2 (quote c) (quote (f g))) ....................................... [PASS]
(and) ................................................................... [PASS]
(or (= 2 2) (> 2 1)) .................................................... [PASS]
(or (= 2 2) (< 2 1)) .................................................... [PASS]
(or (memq (quote b) (quote (a b c))) (/ 3 0)) ........................... [PASS]
(let ((x 2) (y 3)) (* x y)) ............................................. [PASS]
(let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ................... [PASS]
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) .................. [PASS]
(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))  [PASS]
(let ((x (quote (1 3 5 7 9)))) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum)))  [PASS]
(let loop ((numbers (quote (3 -2 1 6 -5))) (nonneg (quote ())) (neg (quote ()))) (cond ((null? numbers) (list nonneg neg)) ((>= (car numbers) 0) (loop (cdr numbers) (cons (car numbers) nonneg) neg)) ((< (car numbers) 0) (loop (cdr numbers) nonneg (cons (car numbers) neg)))))  [PASS]
(quasiquote (list (unquote (+ 1 2)) 4)) ................................. [PASS]
(let ((name (quote a))) (quasiquote (list (unquote name) (quote (unquote name)))))  [PASS]
(quasiquote (a (unquote (+ 1 2)) (unquote-splicing (map abs (quote (4 -5 6)))) b))  [PASS]
(quasiquote (10 5 (unquote (sqrt 4)) (unquote-splicing (map sqrt (quote (16 9)))) 8))  [PASS]
(quasiquote (a (quasiquote (b (unquote (+ 1 2)) (unquote (foo (unquote (+ 1 3)) d)) e)) f))  [PASS]

最後の行で止まったまま半日ほっといても動きません。

細工したのが悪かったのかもと思いstatic版も試して見ましたが状況変わりませんでした。

$ make clean
rm -f *.o *.i *.s
$ make chibi-scheme-static
$ ./chibi-scheme-static tests/r5rs-tests.scm 
((lambda (x) (+ x x)) 4) ................................................ [PASS]
((lambda x x) 3 4 5 6) .................................................. [PASS]
((lambda (x y . z) z) 3 4 5 6) .......................................... [PASS]
(if (> 3 2) (quote yes) (quote no)) ..................................... [PASS]
(if (> 2 3) (quote yes) (quote no)) ..................................... [PASS]
(if (> 3 2) (- 3 2) (+ 3 2)) ............................................ [PASS]
(cond ((> 3 2) (quote greater)) ((< 3 2) (quote less))) ................. [PASS]
(cond ((> 3 3) (quote greater)) ((< 3 3) (quote less)) (else (quote equal)))  [PASS]
(case (* 2 3) ((2 3 5 7) (quote prime)) ((1 4 6 8 9) (quote composite)))  [PASS]
(case (car (quote (c d))) ((a e i o u) (quote vowel)) ((w y) (quote semivowel)) (else (quote consonant)))  [PASS]
(and (= 2 2) (> 2 1)) ................................................... [PASS]
(and (= 2 2) (< 2 1)) ................................................... [PASS]
(and 1 2 (quote c) (quote (f g))) ....................................... [PASS]
(and) ................................................................... [PASS]
(or (= 2 2) (> 2 1)) .................................................... [PASS]
(or (= 2 2) (< 2 1)) .................................................... [PASS]
(or (memq (quote b) (quote (a b c))) (/ 3 0)) ........................... [PASS]
(let ((x 2) (y 3)) (* x y)) ............................................. [PASS]
(let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ................... [PASS]
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) .................. [PASS]
(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))  [PASS]
(let ((x (quote (1 3 5 7 9)))) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum)))  [PASS]
(let loop ((numbers (quote (3 -2 1 6 -5))) (nonneg (quote ())) (neg (quote ()))) (cond ((null? numbers) (list nonneg neg)) ((>= (car numbers) 0) (loop (cdr numbers) (cons (car numbers) nonneg) neg)) ((< (car numbers) 0) (loop (cdr numbers) nonneg (cons (car numbers) neg)))))  [PASS]
(quasiquote (list (unquote (+ 1 2)) 4)) ................................. [PASS]
(let ((name (quote a))) (quasiquote (list (unquote name) (quote (unquote name)))))  [PASS]
(quasiquote (a (unquote (+ 1 2)) (unquote-splicing (map abs (quote (4 -5 6)))) b))  [PASS]
(quasiquote (10 5 (unquote (sqrt 4)) (unquote-splicing (map sqrt (quote (16 9)))) 8))  [PASS]
(quasiquote (a (quasiquote (b (unquote (+ 1 2)) (unquote (foo (unquote (+ 1 3)) d)) e)) f))  [PASS]

最適化ききすぎなのかも、と思って最適化をかけないようにしてみましたが状況は変わりません。

$ make clean
rm -f *.o *.i *.s
$ make CFLAGS=-O0 chibi-scheme-static
cc -c  -Iinclude -Wall -O2 -g -O0 -o main.o main.c
cc -c  -Iinclude -Wall -O2 -g -O0 -o eval.o eval.c
cc -c  -Iinclude -Wall -O2 -g -O0 -o sexp.o sexp.c
cc -Wall -O2 -g -O0 -static -o chibi-scheme-static main.o eval.o sexp.o   -lm
$ ./chibi-scheme-static tests/r5rs-tests.scm 
((lambda (x) (+ x x)) 4) ................................................ [PASS]
((lambda x x) 3 4 5 6) .................................................. [PASS]
((lambda (x y . z) z) 3 4 5 6) .......................................... [PASS]
(if (> 3 2) (quote yes) (quote no)) ..................................... [PASS]
(if (> 2 3) (quote yes) (quote no)) ..................................... [PASS]
(if (> 3 2) (- 3 2) (+ 3 2)) ............................................ [PASS]
(cond ((> 3 2) (quote greater)) ((< 3 2) (quote less))) ................. [PASS]
(cond ((> 3 3) (quote greater)) ((< 3 3) (quote less)) (else (quote equal)))  [PASS]
(case (* 2 3) ((2 3 5 7) (quote prime)) ((1 4 6 8 9) (quote composite)))  [PASS]
(case (car (quote (c d))) ((a e i o u) (quote vowel)) ((w y) (quote semivowel)) (else (quote consonant)))  [PASS]
(and (= 2 2) (> 2 1)) ................................................... [PASS]
(and (= 2 2) (< 2 1)) ................................................... [PASS]
(and 1 2 (quote c) (quote (f g))) ....................................... [PASS]
(and) ................................................................... [PASS]
(or (= 2 2) (> 2 1)) .................................................... [PASS]
(or (= 2 2) (< 2 1)) .................................................... [PASS]
(or (memq (quote b) (quote (a b c))) (/ 3 0)) ........................... [PASS]
(let ((x 2) (y 3)) (* x y)) ............................................. [PASS]
(let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ................... [PASS]
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) .................. [PASS]
(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i))  [PASS]
(let ((x (quote (1 3 5 7 9)))) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum)))  [PASS]
(let loop ((numbers (quote (3 -2 1 6 -5))) (nonneg (quote ())) (neg (quote ()))) (cond ((null? numbers) (list nonneg neg)) ((>= (car numbers) 0) (loop (cdr numbers) (cons (car numbers) nonneg) neg)) ((< (car numbers) 0) (loop (cdr numbers) nonneg (cons (car numbers) neg)))))  [PASS]
(quasiquote (list (unquote (+ 1 2)) 4)) ................................. [PASS]
(let ((name (quote a))) (quasiquote (list (unquote name) (quote (unquote name)))))  [PASS]
(quasiquote (a (unquote (+ 1 2)) (unquote-splicing (map abs (quote (4 -5 6)))) b))  [PASS]
(quasiquote (10 5 (unquote (sqrt 4)) (unquote-splicing (map sqrt (quote (16 9)))) 8))  [PASS]
(quasiquote (a (quasiquote (b (unquote (+ 1 2)) (unquote (foo (unquote (+ 1 3)) d)) e)) f))  [PASS]

32bit版の環境では問題なく動作しているので、x86_64での問題かと思います。
なのでちょっとデバッガにかけてみました。

$ gdb ./chibi-scheme
(gdb) run tests/r5rs-tests.scm

とやってtestを実行し、

(quasiquote (a (quasiquote (b (unquote (+ 1 2)) (unquote (foo (unquote (+ 1 3)) d)) e)) f))  [PASS]
Program received signal SIGINT, Interrupt.
0x00007fcc2957716d in sexp_allocated_bytes (x=0x7fcc28d751a0) at gc.c:43
43	  res = sexp_type_size_base(t) + len_ptr[0] * sexp_type_size_scale(t);

Ctrl + Cで停止中のtestを止めます。どうやらガベージコレクタみたいです。
試しに継続して再び止めると別の場所に止まります。

(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
sexp_sweep (ctx=<value optimized out>, sum_freed_ptr=0x7fffe61cda50) at gc.c:114
114	            freed = (sexp_uint_t)sexp_car(q) + size;
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00007f10ddda635c in sexp_sweep (ctx=<value optimized out>, sum_freed_ptr=0x7fffe61cda50) at gc.c:133
133	        if (freed > max_freed)
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00007f10ddda615e in sexp_allocated_bytes (x=0x7f10dd5a41a0) at gc.c:43
43	  res = sexp_type_size_base(t) + len_ptr[0] * sexp_type_size_scale(t);

どうやら無限ループでもしている見たいです。

gc.cの該当個所を見るとsexp_sweep()という関数でにはforループとさらにその中にwhileループがありました。試しにforループとwhileループの間にbreakpointを入れてみますがbreakしません。

(gdb) b 92
Breakpoint 1 at 0x7fccd612d249: file gc.c, line 92.
(gdb) c
Continuing.

なのでwhileループの条件部分にbreakpointを入れてみます。するとbreakがかかるので、どうやらこのwhileループが無限ループとなっているようです。

(gdb) b 94
Breakpoint 2 at 0x7fccd612d369: file gc.c, line 94.
(gdb) c
Continuing.

Breakpoint 2, sexp_sweep (ctx=0x7fccd592b1a0, sum_freed_ptr=0x7fffde554de0) at gc.c:94
94	    while (((char*)p) < end) {
(gdb) c
Continuing.

Breakpoint 2, sexp_sweep (ctx=0x7fccd592b1a0, sum_freed_ptr=0x7fffde554de0) at gc.c:94
94	    while (((char*)p) < end) {

条件はpとendの比較です。endはこのwhileループの直前で以下のように代入されており、ループないで不変です。

end = (char*)h->data + h->size;

なのでpが変化していないのか、pの変化がendに一致しないのか、ということになります。pをwatchしてみますが変化はありませんでした。

(gdb) watch p
Watchpoint 3: p
(gdb) c
Continuing.

Breakpoint 2, sexp_sweep (ctx=0x7fccd592b1a0, sum_freed_ptr=0x7fffde554de0) at gc.c:94
94	    while (((char*)p) < end) {
(gdb) c
Continuing.

Breakpoint 2, sexp_sweep (ctx=0x7fccd592b1a0, sum_freed_ptr=0x7fffde554de0) at gc.c:94
94	    while (((char*)p) < end) {
(gdb) c
Continuing.

Breakpoint 2, sexp_sweep (ctx=0x7fccd592b1a0, sum_freed_ptr=0x7fffde554de0) at gc.c:94
94	    while (((char*)p) < end) {

どこを通っているのか一行毎に実行してみます。

(gdb) n
96	      for (r=sexp_cdr(q); r && sexp_pairp(r) && (r<p); q=r, r=sexp_cdr(r))
(gdb) 
98	      if (r == p) {
(gdb) 
102	      size = sexp_heap_align(sexp_allocated_bytes(p));
(gdb) 
103	      if ((! sexp_gc_mark(p)) && (! stack_references_pointer_p(ctx, p))) {
(gdb) 
105	        if (((((char*)q)+(sexp_uint_t)sexp_car(q)) == (char*)p)
(gdb) 
104	        sum_freed += size;
(gdb) 
105	        if (((((char*)q)+(sexp_uint_t)sexp_car(q)) == (char*)p)
(gdb) 
108	          if (r && sexp_pairp(r) && ((((char*)p)+size) == (char*)r)) {
(gdb) 
114	            freed = (sexp_uint_t)sexp_car(q) + size;
(gdb) 
115	            p = (sexp) (((char*)p)+size);

代入されてる?ためにしsizeを見てみると0でした。

(gdb) p size
$1 = 0

ではsizeに対する代入はあるかというと、sexp_heap_align()の返り値を代入しています。

size = sexp_heap_align(sexp_allocated_bytes(p));

これは、以下のようなマクロ関数でした。

#define sexp_heap_align(n) sexp_align(n, 4)

…とやったあたりで手に負えない気がしてリタイアしてしまいました。