考慮する:
using System;
public class Test
{
enum State : sbyte { OK = 0, BUG = -1 }
static void Main(string[] args)
{
var s = new State[1, 1];
s[0, 0] = State.BUG;
State a = s[0, 0];
Console.WriteLine(a == s[0, 0]); // False
}
}
これはどのように説明できますか? これは、x86 JIT で実行しているときに Visual Studio 2015 のデバッグ ビルドで発生します。リリース ビルドまたは x64 JIT で実行している場合は、予想どおり True が出力されます。
コマンドラインから再現するには:
csc Test.cs /platform:x86 /debug
(/debug:pdbonly
そして再現/debug:portable
も/debug:full
します。)
ベストアンサー1
.NET 4 x86 ジッターでコード生成のバグが見つかりました。これは非常に珍しいバグで、コードが最適化されていない場合にのみ失敗します。マシン コードは次のようになります。
State a = s[0, 0];
013F04A9 push 0 ; index 2 = 0
013F04AB mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04AE xor edx,edx ; index 1 = 0
013F04B0 call 013F0058 ; eax = s[0, 0]
013F04B5 mov dword ptr [ebp-4Ch],eax ; $temp1 = eax
013F04B8 movsx eax,byte ptr [ebp-4Ch] ; convert sbyte to int
013F04BC mov dword ptr [ebp-44h],eax ; a = s[0, 0]
Console.WriteLine(a == s[0, 0]); // False
013F04BF mov eax,dword ptr [ebp-44h] ; a
013F04C2 mov dword ptr [ebp-50h],eax ; $temp2 = a
013F04C5 push 0 ; index 2 = 0
013F04C7 mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04CA xor edx,edx ; index 1 = 0
013F04CC call 013F0058 ; eax = s[0, 0]
013F04D1 mov dword ptr [ebp-54h],eax ; $temp3 = eax
; <=== Bug here!
013F04D4 mov eax,dword ptr [ebp-50h] ; a == s[0, 0]
013F04D7 cmp eax,dword ptr [ebp-54h]
013F04DA sete cl
013F04DD movzx ecx,cl
013F04E0 call 731C28F4
最適化されていないコードではよくあることですが、多くの一時変数とコードの重複を伴う、時間のかかる処理です。013F04B8 の命令は注目に値します。ここで、sbyte から 32 ビット整数への必要な変換が行われます。配列取得ヘルパー関数は、State.BUG に等しい 0x0000000FF を返しましたが、値を比較する前に、これを -1 (0xFFFFFFFF) に変換する必要があります。MOVSX 命令は、Sign eXtension 命令です。
013F04CCでも同じことが起こりますが、今回はいいえMOVSX 命令を使用して同じ変換を行います。ここでチップが失敗するのですが、CMP 命令は 0xFFFFFFFF と 0x000000FF を比較しますが、これは誤りです。つまり、これは省略エラーであり、コード ジェネレーターは、同じ sbyte から int への変換を実行するために MOVSX を再度発行できませんでした。
このバグの特に珍しい点は、オプティマイザーを有効にすると正しく動作し、どちらの場合でも MOVSX を使用するように認識されることです。
このバグが長い間検出されなかった理由は、列挙型の基本型として sbyte が使用されていたためと考えられます。これは非常にまれなことです。多次元配列を使用することも有益ですが、その組み合わせは致命的です。
そうでなければ、かなり重大なバグだと思います。どの程度の規模になるかは推測しにくいですが、テストできるのは 4.6.1 x86 ジッターだけです。x64 と 3.5 x86 ジッターは、まったく異なるコードを生成し、このバグを回避します。一時的な回避策としては、列挙型の基本型として sbyte を削除し、それをデフォルトにすることです。整数したがって、符号拡張は必要ありません。
connect.microsoft.com でバグを報告できます。この Q+A にリンクするだけで、必要な情報をすべて提供できます。時間をかけたくない場合はお知らせください。私が対応します。