考慮してください:
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
も再現します。
.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でも同じことが起こりますが、今回はno MOVSX命令があり、同じ変換を行います。そこでチップが落ち、CMP命令は0xFFFFFFFFと0x000000FFを比較し、それは偽です。したがって、これは省略のエラーであり、コードジェネレーターは、同じsbyteからintへの変換を実行するためにMOVSXを再度発行することに失敗しました。
このバグで特に珍しいのは、オプティマイザーを有効にしたときに正しく機能することです。両方のケースでMOVSXを使用するようになりました。
このバグが長い間検出されなかった理由として、列挙型の基本型としてsbyteが使用されていることが考えられます。非常にまれです。多次元配列を使用することも有用であり、組み合わせは致命的です。
それ以外の場合、私は言うだろうかなり重大なバグ。それがどれほど広範囲に及ぶかは推測するのが難しく、テストするのは4.6.1 x86ジッタだけです。 x64および3.5 x86ジッタは非常に異なるコードを生成し、このバグを回避します。一時的な回避策は、sbyteを列挙の基本型として削除し、デフォルトintにすることです。したがって、符号拡張は不要です。
Connect.Microsoft.comでバグを報告できます。このQ + Aにリンクすれば、知っておく必要があることをすべて伝えることができます。あなたが時間を取りたくない場合は私に知らせてください、私はそれの世話をします。
OPの宣言について考えてみましょう。
enum State : sbyte { OK = 0, BUG = -1 }
バグはBUG
が負(-128から-1)で、Stateがsigned byteの列挙型である場合にのみ発生するため、どこかにキャストの問題があると推測し始めました。
これを実行する場合:
Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
Console.WriteLine((byte) State.BUG);
}
出力されます:
255
-1
バグ
255
無視する理由のために(今のところ) s[0, 0]
は評価の前に1バイトにキャストされるため、a == s[0,0]
はfalseです。