Roslyn编译器以零优化函数调用乘法

Mikescher:

昨天我在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>()));

首先,我最初在Windows 7下使用Visual Studio 2015 Update 3(发布模式(/optimize)和调试模式)以及各种目标框架(4.0、4.5、4.6和4.6.1)对它进行了测试。在所有8种情况下,结果都是相同的(1|2|3|0)。

然后,我在Windows 7上使用Visual Studio 2013 Update 5(再次针对发布/调试模式和目标框架的所有组合)进行了测试。令我惊讶的是,这里的陈述并没有被优化,并产生了预期结果1|2|0

因此,我可以得出结论,此行为既不依赖于/optimize目标框架标志也不依赖于目标框架标志,而是依赖于所使用的编译器版本。

出于兴趣,我用C ++编写了类似的代码,并使用当前的gcc版本进行了编译。在这里,没有优化乘以零的函数调用,并且该函数已正确执行。

我认为这样的优化只有在stack.Pop()具有纯函数的情况下才有效(它肯定不是)。但是我不愿意将其称为错误,我认为这只是我不知道的功能?

是否在任何地方都记录了此“功能”,并且有(简便)方法可以禁用此优化吗?

VSadov:

是的,绝对是一个错误。<expr> * 0,如果<expr>具有副作用,则不应将其优化为0。

感谢您报告问题!!

您可以在https://github.com/dotnet/roslyn/issues/13486跟踪错误/修复的进度

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章