2016-08-31 21 views
21

Dün ile işlev çağrısı çarpma uzakta optimize benim C# kodu bu garip davranışı bulundu: Roslyn derleyici sıfır

Stack<long> s = new Stack<long>(); 

s.Push(1);   // stack contains [1] 
s.Push(2);   // stack contains [1|2] 
s.Push(3);   // stack contains [1|2|3] 

s.Push(s.Pop() * 0); // stack should contain [1|2|0] 

Console.WriteLine(string.Join("|", s.Reverse())); 

Ben programı 1|2|0 yazdırmak üstlendi ama aslında 1|2|3|0 baskılı.

Stack<long> s = new Stack<long>(); 
s.Push(1L); 
s.Push(2L); 
s.Push(3L); 
s.Push(0L); // <- the offending line 
Console.WriteLine(string.Join<long>("|", s.Reverse<long>())); 

İlk Başlangıçta altında bu test:

// ... 
IL_0022: ldloc.0 
IL_0023: ldc.i4.0 
IL_0024: conv.i8 
IL_0025: callvirt instance void class [System]System.Collections.Generic.Stack`1<int64>::Push(!0) 
// ... 

ILSpy Decompilation: Eğer s.Pop() * 0 basitçe 0 için optimize edilmiştir görebilirsiniz (ILSpy aracılığıyla) oluşturulan IL koduna baktığımızda

Her ikisi de Serbest Bırakma modu (/optimize) ve Hata Ayıklama modu ve çeşitli hedef çerçevelerle (4.0, 4.5, 4.6 ve 4.6.1) Visual Studio 2015 Güncelleştirmesi 3 ile Windows 7. Tüm 8 vakada sonuç aynıydı (1|2|3|0).

Sonra Windows 7 altında Visual Studio 2013 Güncelleştirmesi 5 ile (yine tüm Release/Debug modunun ve hedef çerçevenin birleşimleriyle) test ettim. Benim sürprizime göre, no'lu adreste no'lu adres en iyi duruma getirilmiş ve beklenen sonucu 1|2|0 olarak vermiştir.

Bu nedenle, bu davranışın ne /optimize ne de hedef çerçeve bayrağına değil, kullanılan derleyici sürümüne bağlı olduğu sonucuna varabilirim.

Dışı C++ 'da benzer bir kod yazdım ve mevcut gcc sürümü ile derledim. Burada sıfır ile çarpılan bir işlev çağrısı optimize edilmez ve işlev düzgün bir şekilde yürütülür.

Böyle bir optimizasyonun yalnızca stack.Pop()'un salt bir işlev olması durumunda geçerli olacağını düşünüyorum (ki bu kesinlikle değil). Ama buna bir hata demeye tereddüt ediyorum, sanırım benim için bilinmeyen bir özellik mi?

Bu "özellik" her yerde belgelenmiştir ve bu optimizasyonu devre dışı bırakmak için kolay bir yol var mı?

+4

Yuck de hata/düzeltme ilerlemesini takip edebilir, evet, VS2015 güncelleştirme 2 ile üreme olabilir Roslyn bir büyük hata jeneratör olmuştur. Bildirmek için [Yeni Sayı düğmesi] 'ni (https://github.com/dotnet/roslyn/issues) tıklayın. Pop dönüş değerini saklamak için bir değişken kullanarak sınırlayın, jitter optimizer tarafından tekrar ortadan kaldırılacaktır. –

+0

Bu, büyük olasılıkla, site dışı bir kaynağa bağlantı olmadan yanıtlanamaz (bu konuya yer verir). Burada gönderilmesi gereken harika bir hata raporu https://github.com/dotnet/roslyn/issues Elbette, takımdaki birisi cevap verebilir ... belki de Jared Parsons? – Will

+0

@HansPassant: Bunun bir hata değil, bir özellik olduğunu varsaydım :). Ama eğer bir sorun çıkartacağım ve kod-jeneratörü şimdi kendi satırında Stack.Pop() 'i açıkça çağırmak için değiştireceğim. – Mikescher

cevap

12

Evet, kesinlikle bir hatadır. < expr> * 0, < expr> 'nin yan etkilere sahip olması durumunda 0'a optimize edilmemelidir.

Sorunu bildirdiğiniz için teşekkür ederiz!

Sen https://github.com/dotnet/roslyn/issues/13486

+1

Bu gibi konularda çok duyarlı olduğun için teşekkürler. – Will

+1

Düzeltme, https://github.com/dotnet/roslyn/pull/13501 – VSadov

+0

Wow'da birleştirildi, bu harika. MS gelişiminin son zamanlarda nasıl gittiği konusunda gerçekten mutluyum. Siz çocuklar telefon grubuna gidip fındıklara meydan okuyabilir misiniz? WP platformundan korkuyorum. Cidden, bunun için korkuyorum. – Will

İlgili konular