I think I'm observing the .NET JIT compiler not inlining or optimizing away calls to empty static methods that have no side effects, which is a bit surprising given some bespoken online resources.
My environment is Visual Studio 2013 on x64, Windows 8.1, .NET Framework 4.5.
Given this simple test program (https://ideone.com/2BRCpC)
class Program { static void EmptyBody() { } static void Main() { EmptyBody(); } }
A release build with optimizations of the above program produces the following MSIL for Main
and EmptyBody
:
.method private hidebysig static void Main() cil managed { .entrypoint // Code size 6 (0x6) .maxstack 8 IL_0000: call void Program::EmptyBody() IL_0005: ret } // end of method Program::Main .method private hidebysig static void EmptyBody() cil managed { // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method Program::EmptyBody
It's not surprising that the MSIL contains a call from Main
to EmptyBody
, since the C# compiler isn't expected to inline or optimize away calls like that. However, I thought that the JIT compiler would then inline or optimize away that call. But that doesn't seem to happen.
If I run the above program and break into the debugger in Main
, the generated assembly is this:
00572621 mov ebp,esp 00572623 cmp dword ptr ds:[4320B84h],0 0057262A je 00572631 0057262C call 73E6AF20 00572631 call dword ptr ds:[4321578h]
The instruction pointer is immediately set to the last line at 00572631, which is the call to EmptyBody
. Stepping into EmptyBody
, the generated assembly is found to be
00BD2651 mov ebp,esp 00BD2653 cmp dword ptr ds:[4B00B84h],0 00BD265A je 00BD2661 00BD265C call 73E6AF20 00BD2661 nop 00BD2662 pop ebp 00BD2663 ret
The instruction pointer is immediately set to the nop
line at 00BD2661, which doesn't do anything, and I cannot guess why it's generated in the first place.
Given that the two assembly snippets above share the same 4-instruction header, I assume that's just the regular method entry boiler plate where the stack and such is set up. I'm keen to learn to know what these recurring instructions would do, though:
00BD2653 cmp dword ptr ds:[4B00B84h],0 00BD265A je 00BD2661 00BD265C call 73E6AF20
Anyhow, the main question is: Why does the JIT compiler produce assembly that calls the empty-bodied static method EmptyBody
?
After digging a bit further, it turns out I can answer this question myself. As explained at http://blogs.msdn.com/b/vancem/archive/2006/02/20/535807.aspx , observing the disassembly of an optimized release build under the debugger will by default affect the JIT compiler.
Unchecking these
under VS > Tools > Debugging > General, will show the "real" JIT compilation result, which for the call to EmptyBody
in my Main
above is this:
004C2620 ret
Meaning that the call to EmptyBody
is completely removed, which is what was expected and the world is still a happy and somewhat predictable place to live in :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With