Visual Studio 2005 の「最適化付き x64 build」で不正なコードが生成される問題について
コミケ直前にこの問題に遭遇して、x64 build のリリース大丈夫かとヒヤヒヤしましたが…^^;
さて、この問題について検証してみました。
テストコードとして次のコードを打ち*1、x64 build 最適化付き (/O2, /Ox) でビルドしてみると可憐に落ちます。
enum ETest
{
TEST_FAILED = INT_MIN,
TEST_F_HOGE1,
TEST_F_HOGE2,
TEST_F_MAX,
TEST_SUCCEEDED = 0,
TEST_S_HOGE1,
TEST_S_HOGE2,
TEST_S_MAX,
};
const int g_anArray[] =
{
1, 2, 3, 4
};
// main にある eTest を *非*コンパイル時定数にする為に
// noline にする
__declspec(noinline) ETest GetValue()
{
return TEST_FAILED;
}
int _tmain(int argc, _TCHAR* argv[])
{
const ETest eTest = GetValue();
__int64 n = g_anArray[eTest - INT_MIN];
printf("%I64d", n);
getchar();
return 0;
}
で、↓が混合モードのコード
int _tmain(int argc, _TCHAR* argv[])
{
0000000000401010 sub rsp,28h
const ETest eTest = GetValue();
0000000000401014 call GetValue (401000h)
__int64 n = g_anArray[eTest - INT_MIN];
0000000000401019 lea rdx,[4021B0h]
printf("%I64d", n);
0000000000401020 movsxd rcx,eax
0000000000401023 mov rax,0FFFFFFFE00000000h
000000000040102D add rax,rdx
0000000000401030 movsxd rdx,dword ptr [rax+rcx*4] ; ココで落ちる
0000000000401034 lea rcx,[string "%I64d" (4021C0h)]
000000000040103B call qword ptr [__imp_printf (402130h)]
getchar();
0000000000401041 call qword ptr [__imp_getchar (402128h)]
return 0;
0000000000401047 xor eax,eax
}
0000000000401049 add rsp,28h
000000000040104D ret
落ちた所の dword ptr [rax+rcx*4] を見ると、
- rax に g_anArray へのポインタ
- rcx に 添え字
見たいなのが来るべきなんだと思います。が、落ちた時のレジスタの内容 を見てみると…
- rax = FFFFFFFE004021B0
- rcx = FFFFFFFF80000000
計算すると、アドレス FFFFFFFC004021B0 を参照してます…って、おまえ何処見てるねん! そりゃ落ちるわ。
じゃぁ、何がおかしいのかなー……と、いろいろ調査してみた結果、おそらく 0000000000401020 の "movsxd rcx,eax" がおかしいんだと思います。
movsxd と符号拡張付きmove が使われてますが、movzx*2 のゼロ拡張付きmove が正解だと思います。
この仮定で計算してみると…
movzx rcx,eax ; rcx = 0x00000000_80000000
mov rax,0FFFFFFFE00000000h ;
add rax,rdx ; rax = 0xFFFFFFFE_004021B0 -> g_anArray + 0xFFFFFFFE_00000000
movsxd rdx,dword ptr [rax+rcx*4] ;
; rcx * 4 = 0x00000002_00000000
; rcx * 4 + 0xFFFFFFFE_00000000 = 0x00000000_00000000 ゆえに
; rcx * 4 + rax = 0x00000000_004021B0 -> OK!
OK、結論。
んもー Microsoft おっちょこちょいさん! あはははははははははははははは(右手をグーにして振り上げながら
添え字に負数と減算が絡んでくるようなコード書いてる人は、ちょっと注意かもしれません。