はい、私はこれをreproします。まあ、ほとんど。私は実際には0の出力は得られませんが、他のいくつかのガベージ出力があります。だから私は無効な行動を再現することができ、私は原因を特定しました。
-m64 -mno-sse
フラグhere on Goldbolt's Compiler ExplorerでGCC 5.4.0が生成するコードを見ることができます。特に、これは私たちが気にする指示です:
// double pi = 3.14;
fld QWORD PTR .LC0[rip]
fstp QWORD PTR [rbp-8]
// std::cout << "pi:";
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
// std::cout << pi;
sub rsp, 8
push QWORD PTR [rbp-8]
mov rdi, rax
call std::basic_ostream<char, std::char_traits<char> >::operator<<(double)
add rsp, 16
ここには何が起こっていますか?さて、まず、-mno-sse
フラグの意味を理解する必要があります。これにより、コンパイラはSSE命令(およびそれ以降の命令セット拡張機能)を使用するコードを生成できなくなります。したがって、すべての浮動小数点演算は、従来のx87 FPUを使用して実行する必要があります。これはうまく動作し、32ビットビルドでは十分にサポートされていますが、64ビットビルドでは無意味です。 AMD64仕様ではSSE2のサポートが最低限必要であるため、すべて 64ビット対応x86 CPUはSSEとSSE2の両方をサポートします。この前提でthe ABIになりました。x86-64のすべての浮動小数点演算はSSE2命令を使用して実行され、浮動小数点値はXMMレジスタに渡されます。したがって、浮動小数点演算を実行するが、コンパイラがSSE/SSE2命令を使用することを禁止すると、コード生成プログラムは不可能な位置に置かれ、必然的に失敗することになります。
どのように正確に失敗しますか?上のコードを見てみましょう。これは最適化されていません(最適化フラグを渡さなかったためデフォルトで-O0
になりました)。これは読みにくくなりますが、私には負担になります。
最初のブロックでは、x87 FPU命令を使用して、倍精度浮動小数点値(3.14)をメモリからロードします(バイナリの定数として格納されます)。スタック。次に、その値をスタックからポップし、メモリに格納します(プログラムスタック)。これは、最適化されていないコードで実行されているだけでも忙しい作業です。ほとんど無視できます。ここでの浮動小数点値は、浮動小数点値がメモリにrbp-8
(ベースポインタから8バイトのオフセット)に格納されていることです。
次の命令ブロックは完全に無視することができます。彼らは単に文字列 "pi:"を出力します。
命令の第3ブロックは、であり、浮動小数点値を出力すると仮定すると、となります。まず、8バイトのスペースがスタックに割り当てられます。次に、以前にメモリに格納していた浮動小数点値がスタックにプッシュされます。
これまでのところ、とても良いです。これは、が通常の場合、浮動小数点パラメータを関数に渡します。つまり、x87命令を使用していた32ビットABIに続いて、32ビットのビルドに渡します。64ビットのビルドでは、64ビットのABIに続いて、浮動小数点パラメータがXMMレジスタに渡され、これがoperator<<(double)
関数がそのパラメータを受け取る予定の場所です。 でもSSEコードは生成できないので、XMMレジスタを使用することはできません。その手は結ばれている。あなたの特定のオプションがABIのを壊すので、それはABIに続くライブラリ関数を適切に呼び出すことができません。
ここからはすべて下り坂です。コンパイラは、rax
レジスタの内容をrdi
レジスタにコピーし、次にoperator<<(double)
関数を呼び出します。この関数は、XMM0
レジスタに渡された浮動小数点値をstdoutに書き込もうとしますが、そのレジスタにはガベージが含まれています(実際には内容は正式には未定義です)、このガベージはstdout 、あなたが見ることが期待される浮動小数点値の代わりに。
問題を理解したので、解決策は何ですか?
- あなたが
-m32
フラグを使用してコンパイルする32ビットのバイナリを強制する、SSE命令を使用しない場合。これは安全に-mno-sse
と組み合わされています。
- 64ビットバイナリが必要な場合は、
-mno-sse
フラグを渡さないでください。これは、64ビットABIに違反しているためです。これはSSE2のサポートが最小限であることを前提としています。
(私はここでそれを無視していますが、-m64
フラグと一緒に-mno-sse
フラグを渡すことが技術的に合理的である。Linuxカーネルのコードをコンパイルするために使用されているため、実際に、これは明示的にGCCによってサポートされていますこの場合、XMMレジスタの状態は呼び出し間で保持されません。これは、カーネルコードが浮動小数点演算を実行しないためにのみ機能します。浮動小数点演算と関係がある)。
あなたの左手は右手が何であるかを知らないすべてのランタイムサポートライブラリも再構築する必要があります。これは大きな急いで無意味になります。 –