Mosh JIT で R6RS test suite が SEGV する原因はアラインメントだった

タイトルの通り落ちると思ったらアラインメントが原因だった。はまらずにすぐに原因を発見できた自分をほめたい。

0x000000000045c0ec in scheme::Flonum::iDiv (theVM=0xa20c80, argc=2, argv=<value optimized out>) at src/Flonum.h:375
375             return f2 > 0.0 ? ::floor(f1 / f2) : - ::floor(f1 / (-f2));

0x000000000045c0e8 <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+472>:     xorpd  %xmm1,%xmm2
0x000000000045c0ec <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+476>:     movapd %xmm1,0x10(%rsp) <== ここ
0x000000000045c0f2 <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+482>:     divsd  %xmm2,%xmm0

最初はスタックオーバーフローかと思ったのだがよく調べると違った。マニュアルによると movapd は 16 byte アラインメントを要求するらしい。

(gdb) print $rsp + 0x10
$2 = (void *) 0x7fffffffbf28

これはだめだ。落ちているところは gccコンパイルした関数なので勝手にアラインメントしてくれれば良いのにと思って調べる。
いつもお世話になっている herumi さんのx64 Assembly Language Programmingで見てみると

gccでのスタック
スタックは常に16byteアラインメントされています. 
ただし関数呼び出し直後は戻りアドレス(8byte)がpushされているため, 8(mod 16)となっています.

とある。つまり
gcc 関数 => jit コード => gcc 関数のように call していって、jit コードで 16 byte アラインメントが守られなくなってしまったのだろう。
jitrsp をいじるのは専ら push/pop だけなので奇数個の push を偶数にすれば良かろうと思っているが今日はここまで。


自動で整列させるコードがありそうなので今度探そう。