Çok boyutlu bir dizideki numaralandırma değeri neden kendisine eşit değil?


151

Düşünmek:

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
    }
}

Bu nasıl açıklanabilir? X86 JIT'de çalışırken Visual Studio 2015'te hata ayıklama yapılarında oluşur. X64 JIT'de bir sürüm derlemesi veya çalışması beklendiği gibi True yazdırır.

Komut satırından çoğaltmak için:

csc Test.cs /platform:x86 /debug

( /debug:pdbonly, /debug:portableVe /debug:fullaynı zamanda yeniden.)


2
ideone.com/li3EzY Doğru. .net versiyonu hakkında daha fazla bilgi ekle, IDE, derleyici
Backs

1
Burada aynı. Ancak proje ayarlarıyla uğraştıktan sonra, "Derleme" sekmesindeki "32 bit" onay kutusunun işaretini kaldırmanın amaçlandığı gibi çalıştığını ve doğru döndüğünü anladım. Yani, bana bir WoW64 sorunu gibi görünüyor.
Dmitry Rotay

2
Görünüşe göre çerçevede bir hata işaret ettiniz.
Fabien PERRONNET

1
İlginç bir şekilde, kırık kodu üzerinden çalışan ildasmve sonra ilasm"düzeltir" ...
Jon Skeet

2
/debug=IMPLKırık bayrak yaprakları; /debug=OPT"düzeltir".
Jon Skeet

Yanıtlar:


163

.NET 4 x86 titreşiminde bir kod oluşturma hatası buldunuz. Çok alışılmadık bir durum, sadece kod optimize edilmediğinde başarısız oluyor. Makine kodu şuna benzer:

        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  

Optimize edilmemiş kod için normal olan çok sayıda geçici ve kod çoğaltma içeren bir plodding ilişkisi. 013F04B8'deki talimat dikkat çekicidir, yani sbyte'den 32 bitlik bir tam sayıya gerekli dönüşüm gerçekleşir. Dizi alıcısı yardımcı işlevi, State.BUG'a eşit olan 0x0000000FF döndürdü ve değer karşılaştırılmadan önce -1'e (0xFFFFFFFF) dönüştürülmesi gerekiyor. MOVSX komutu bir Sign eXtension talimatıdır.

Aynı şey tekrar 013F04CC'de olur, ancak bu sefer hiçbir şey yok aynı dönüşümü yapacak MOVSX komutu . Çiplerin düştüğü yer, CMP komutu 0xFFFFFFFF ile 0x000000FF'yi karşılaştırır ve bu yanlıştır. Bu bir ihmal hatasıdır, kod üreteci aynı sbyte int dönüşümü gerçekleştirmek için MOVSX'i tekrar yayamadı.

Bu hata hakkında özellikle sıra dışı olan şey, optimize ediciyi etkinleştirdiğinizde bunun doğru çalışmasıdır, şimdi her iki durumda da MOVSX'i kullanmayı biliyor.

Bu hatanın bu kadar uzun süre fark edilmemesinin olası nedeni, sümenin enumun temel türü olarak kullanılmasıdır. Yapmak oldukça nadir. Çok boyutlu bir dizi kullanmak da yararlıdır, kombinasyon ölümcüldür.

Aksi takdirde oldukça kritik bir hata diyebilirim. Ne kadar yaygın olabileceğini tahmin etmek zor, test etmek için sadece 4.6.1 x86 titreşim var. X64 ve 3.5 x86 titreşimi çok farklı kodlar oluşturur ve bu hatayı önler. Devam etmek için geçici geçici çözüm, enum base türü olarak sbyte'yi kaldırmak ve varsayılan, int , bu nedenle herhangi bir işaret uzantısı gerekmez.

Connect.microsoft.com adresindeki hatayı dosyalayabilirsiniz, bu Q + A'ya bağlantı vermek bilmeleri gereken her şeyi anlatmak için yeterli olmalıdır. Zaman ayırmak istemiyorsanız bana bildirin, ben de ilgilenirim.


33
Böyle garip bir sorunun kesin nedeni ile iyi, sağlam veriler, her zaman okumaktan zevk, +1.
Lasse V. Karlsen

11
Oy verebilmemiz için lütfen connect.microsoft.com makalesine bir bağlantı gönderin.
Hans Passant

Bunun byteyerine kullanarak sbyteçok iyi olmalı ve gerçek kod ile veritabanında bayraklarınızı fazladan yer almak istemiyorum bir ORM demek için tercih edilebileceğini varsayalım .
Voo

6
Bağlanmak yerine dotnet / coreclr'a hata gönderirim , doğrudan JIT geliştiricilerine ulaşırsınız .
Lucas Trzesniewski

8
Microsoft'ta JIT ekibinde bir geliştiriciyim. Hatayı yeniden oluşturdum ve dahili olarak bunun için bir sorun açtım (nakliye x86 JIT henüz github'da açık değil). Bunun ne zaman düzeltileceği zamanlaması açısından, bu düzeltmenin araçların bir sonraki büyük sürümüne dahil edileceğini tahmin ediyorum. Bu hatanın ticari etkisi varsa ve daha önce bir düzeltmeye ihtiyacınız varsa, lütfen etkiyi ve daha hızlı bir şekilde düzeltmek için hangi alternatiflere ihtiyacımız olduğunu görebilmemiz için connect (connect.microsoft.com) sorununu dosyalayın.
Russell C. Hadley

8

OP'nin beyanını ele alalım:

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

Hata yalnızca BUGnegatif olduğunda (-128'den -1'e kadar) ve Durum işaretli baytın bir numaralandırması olduğu için sayımı olduğu için bir yerde döküm sorunu olduğunu varsaymaya başladım.

Bunu çalıştırırsanız:

Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
    Console.WriteLine((byte) State.BUG);
}

çıktı olacak:

255

-1

BUG

255

Göz ardı ettiğim bir nedenden dolayı (şu andan itibaren) s[0, 0] değerlendirmeden önce bir bayta atıldı ve bu yüzden a == s[0,0]yanlış olduğunu iddia ediyor .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.