2016-08-31 99 views
21

昨天函數調用乘法,我發現這種奇怪的行爲在我的C#代碼:羅斯林編譯器優化掉零

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())); 

我認爲該計劃將打印1|2|0但實際上它印1|2|3|0

看着所產生的IL代碼(通過ILSpy)可以看到s.Pop() * 0被優化,以簡單地0

// ... 
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反編譯

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>())); 

首先我最初下測試此包含發佈模式(/optimize)和調試模式以及各種目標框架(4.0,4.5,4.6和4.6.1)的Visual Studio 2015 Update 3的Windows 7。在所有8例中,結果相同(1|2|3|0)。

然後,我使用Visual Studio 2013 Update 5在Windows 7下進行了測試(再次使用Release/Debug模式和目標框架的所有組合)。令我驚訝的是,這裏的發言是而不是優化,併產生預期結果1|2|0

所以我可以斷定這個行爲既不取決於/optimize也不取決於目標框架標誌而取決於所使用的編譯器版本。

出於興趣,我用C++編寫了一個類似的代碼,並使用當前的gcc版本編譯它。這裏一個乘以零的函數調用沒有被優化,並且該函數被正確執行。

我認爲這樣的優化只有在stack.Pop()是純函數(它肯定不是)時纔有效。但是我很猶豫要把這個稱爲一個bug,我認爲這只是我未知的一個特性?

這個「功能」在任何地方都有記錄,並且是否有一種(簡單)方法來禁用此優化?

+4

呸的進展,是的,我可以用VS2015更新2.瑞普羅斯林一直是一大錯誤產生。點擊[New Issue按鈕](https://github.com/dotnet/roslyn/issues)進行報告。通過使用變量來存儲Pop返回值,它將被抖動優化器再次消除。 –

+0

如果沒有指向非現場資源的鏈接,這很有可能無法回答(這使得此主題無效)。這是一個很棒的bug報告,應該在這裏提交https://github.com/dotnet/roslyn/issues當然,團隊中的某個人可能會回答......也許Jared Parsons? – Will

+0

@HansPassant:我有點認爲這不是一個錯誤,而是一個功能:)。但如果是這樣的話,我會打開一個問題,現在更改我的代碼生成器,以便在它自己的行中顯式調用Stack.Pop()。 – Mikescher

回答

12

是的,這絕對是一個錯誤。 < expr> * 0,如果< expr>有副作用,則不應優化爲0。

感謝您報告問題!

您可以跟蹤bug /修復在 https://github.com/dotnet/roslyn/issues/13486

+1

感謝你們對這類問題的迴應。 – Will

+1

該修復已合併在https://github.com/dotnet/roslyn/pull/13501 – VSadov

+0

哇,這太棒了。我真的很開心MS發展的最新進展。你們可以去電話小組,把他們踢進堅果嗎?我擔心WP平臺。說真的,我很害怕它。 – Will