昨天函數調用乘法,我發現這種奇怪的行爲在我的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,我認爲這只是我未知的一個特性?
這個「功能」在任何地方都有記錄,並且是否有一種(簡單)方法來禁用此優化?
呸的進展,是的,我可以用VS2015更新2.瑞普羅斯林一直是一大錯誤產生。點擊[New Issue按鈕](https://github.com/dotnet/roslyn/issues)進行報告。通過使用變量來存儲Pop返回值,它將被抖動優化器再次消除。 –
如果沒有指向非現場資源的鏈接,這很有可能無法回答(這使得此主題無效)。這是一個很棒的bug報告,應該在這裏提交https://github.com/dotnet/roslyn/issues當然,團隊中的某個人可能會回答......也許Jared Parsons? – Will
@HansPassant:我有點認爲這不是一個錯誤,而是一個功能:)。但如果是這樣的話,我會打開一個問題,現在更改我的代碼生成器,以便在它自己的行中顯式調用Stack.Pop()。 – Mikescher