多次元配列の列挙値がそれ自体と等しくないのはなぜですか?


151

検討してください:

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も再現します。)


2
ideone.com/li3EzYそれは本当です。.NETバージョンに関する詳細情報を追加し、IDE、コンパイラは
バックス

1
こっちも一緒。しかし、プロジェクトの設定をいじった後、[ビルド]タブの[32ビットを優先する]チェックボックスをオフにすると、意図したとおりに機能し、trueが返されることがわかりました。だから、私にはWoW64の問題のように見えます。
Dmitry Rotay 2016

2
フレームワークのバグを指摘したようです。
Fabien PERRONNET 2016

1
興味深いことに、壊れたコードを実行し、ildasmそれをilasm「修正」します...
Jon Skeet

2
/debug=IMPLそれが壊れたフラグの葉; /debug=OPTそれを「修正」します。
Jon Skeet

回答:


163

.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を削除し、それをデフォルトのintにすることですにすることです。そのため、符号拡張は必要ありません。

connect.microsoft.comでバグを報告することができます。このQ + Aにリンクすることで、彼らが知る必要のあるすべてを伝えることができます。時間をかけたくない場合はお知らせください。面倒を見てくれます。


33
そのような奇妙な問題の正確な原因を含む、しっかりとしたデータ。
Lasse V. Karlsen

11
connect.microsoft.comの記事へのリンクを投稿して、投票できるようにしてください。
ハンスパッサント2016

byte代わりにsbyteを使用することも問題ないと思います。実際のコードがORMと共に使用され、データベース内のフラグが余分なスペースを占有しないようにする場合は、好ましいかもしれません。
Voo

6
接続ではなくdotnet / coreclrにバグ投稿します。JIT開発者に直接連絡します。
Lucas Trzesniewski

8
私はマイクロソフトのJITチームの開発者です。バグを再現し、内部で問題を公開しました(x86 JITの出荷はまだgithubで公開されていません)。これが修正される時期については、ツールの次のメジャーリリースにこの修正が含まれる予定です。このバグがビジネスに影響を与えており、以前に修正が必要な場合は、接続(connect.microsoft.com)の問題を提出してください。これにより、修正とより迅速に修正を入手するために必要な代替案を確認できます。
ラッセルC.ハドリー

8

OPの宣言について考えてみましょう。

enum State : sbyte { OK = 0, BUG = -1 }

バグBUGは負の場合(-128から-1まで)にのみ発生し、状態は符号付きバイトのため、どこかにキャストの問題があると想定し始めました。

これを実行すると:

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]は、評価の前にバイトにキャストされ、それa == s[0,0]がfalseであると主張する理由です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.