c# default - Roslyn compiler optimizing away function call multiplication with zero

1 Answers

Yes, it is definitely a bug. < expr > * 0, should not be optimized into 0 if < expr > has sideeffects.

Thanks for reporting the issue!!

You can track the progress of the bug/fix at https://github.com/dotnet/roslyn/issues/13486

methods add

Yesterday I found this strange behavior in my C# code:

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

I assumed the program would print 1|2|0 but in fact it printed 1|2|3|0.

Looking at the generated IL code (via ILSpy) you can see that s.Pop() * 0 is optimized to simply 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 decompilation:

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

First I tested this initially under Windows 7 with Visual Studio 2015 Update 3 with both Release mode (/optimize) and Debug mode and with various target frameworks (4.0, 4.5, 4.6 and 4.6.1). In all 8 cases the result was the same (1|2|3|0).

Then I tested it under Windows 7 with Visual Studio 2013 Update 5 (again with all the combinations of Release/Debug mode and target framework). To my surprise the statement is here not optimized away and yields the expected result 1|2|0.

So I can conclude that this behavior is neither dependent on /optimize nor the target framework flag but rather on the used compiler version.

Out of interest I wrote a similar code in C++ and compiled it with the current gcc version. Here a function call multiplied with zero is not optimized away and the function is properly executed.

I think such an optimization would only be valid if stack.Pop() were a pure function (which it definitely isn't). But I'm hesitant to call this a bug, I assume it's just a feature unknown to me?

Is this "feature" anywhere documented and is there an (easy) way to disable this optimization?


c# optimization visual-studio-2015 roslyn