I am using Mono.Cecil.dll to decompile a VS 2015 app. During decompilation I noticed that there are 2 instructions (stloc.0 & ldloc.0) that are missing. This did not happen when I decompiled a VS 2013 application.
Is there a problem with the decompiler or Visual Studio 2015 complier has changed?
Update:
I have decompiled the code using ILDasm. Here is what can be found in 2015 and 2013 in Initialize component. The problem still appears:
2015
.method private hidebysig instance void InitializeComponent() cil managed
{
// Code size 125 (0x7d)
.maxstack 4
IL_0000: ldtoken VS2015DotNet4test.Form1
IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000a: newobj instance void [System]System.ComponentModel.ComponentResourceManager::.ctor(class [mscorlib]System.Type)
IL_000f: ldarg.0
IL_0010: newobj instance void [System.Windows.Forms]System.Windows.Forms.Label::.ctor()
IL_0015: stfld class [System.Windows.Forms]System.Windows.Forms.Label VS2015DotNet4test.Form1::label1
IL_001a: ldarg.0
IL_001b: call instance void [System.Windows.Forms]System.Windows.Forms.Control::SuspendLayout()
IL_0020: dup
IL_0021: ldarg.0
IL_0022: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2015DotNet4test.Form1::label1
IL_0027: ldstr "label1"
IL_002c: callvirt instance void [System]System.ComponentModel.ComponentResourceManager::ApplyResources(object,
string)
IL_0031: ldarg.0
IL_0032: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2015DotNet4test.Form1::label1
IL_0037: ldstr "label1"
IL_003c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
IL_0041: ldarg.0
IL_0042: ldstr "$this"
IL_0047: callvirt instance void [System]System.ComponentModel.ComponentResourceManager::ApplyResources(object,
string)
IL_004c: ldarg.0
IL_004d: ldc.i4.1
IL_004e: call instance void [System.Windows.Forms]System.Windows.Forms.ContainerControl::set_AutoScaleMode(valuetype [System.Windows.Forms]System.Windows.Forms.AutoScaleMode)
IL_0053: ldarg.0
IL_0054: call instance class [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection [System.Windows.Forms]System.Windows.Forms.Control::get_Controls()
IL_0059: ldarg.0
IL_005a: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2015DotNet4test.Form1::label1
IL_005f: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection::Add(class [System.Windows.Forms]System.Windows.Forms.Control)
IL_0064: ldarg.0
IL_0065: ldstr "Form1"
IL_006a: call instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
IL_006f: ldarg.0
IL_0070: ldc.i4.0
IL_0071: call instance void [System.Windows.Forms]System.Windows.Forms.Control::ResumeLayout(bool)
IL_0076: ldarg.0
IL_0077: call instance void [System.Windows.Forms]System.Windows.Forms.Control::PerformLayout()
IL_007c: ret
} // end of method Form1::InitializeComponent
2013
.method private hidebysig instance void InitializeComponent() cil managed
{
// Code size 127 (0x7f)
.maxstack 3
.locals init (class [System]System.ComponentModel.ComponentResourceManager V_0)
IL_0000: ldtoken VS2013DotNet4test.Form1
IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000a: newobj instance void [System]System.ComponentModel.ComponentResourceManager::.ctor(class [mscorlib]System.Type)
IL_000f: stloc.0
IL_0010: ldarg.0
IL_0011: newobj instance void [System.Windows.Forms]System.Windows.Forms.Label::.ctor()
IL_0016: stfld class [System.Windows.Forms]System.Windows.Forms.Label VS2013DotNet4test.Form1::label1
IL_001b: ldarg.0
IL_001c: call instance void [System.Windows.Forms]System.Windows.Forms.Control::SuspendLayout()
IL_0021: ldloc.0
IL_0022: ldarg.0
IL_0023: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2013DotNet4test.Form1::label1
IL_0028: ldstr "label1"
IL_002d: callvirt instance void [System]System.ComponentModel.ComponentResourceManager::ApplyResources(object,
string)
IL_0032: ldarg.0
IL_0033: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2013DotNet4test.Form1::label1
IL_0038: ldstr "label1"
IL_003d: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
IL_0042: ldloc.0
IL_0043: ldarg.0
IL_0044: ldstr "$this"
IL_0049: callvirt instance void [System]System.ComponentModel.ComponentResourceManager::ApplyResources(object,
string)
IL_004e: ldarg.0
IL_004f: ldc.i4.1
IL_0050: call instance void [System.Windows.Forms]System.Windows.Forms.ContainerControl::set_AutoScaleMode(valuetype [System.Windows.Forms]System.Windows.Forms.AutoScaleMode)
IL_0055: ldarg.0
IL_0056: call instance class [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection [System.Windows.Forms]System.Windows.Forms.Control::get_Controls()
IL_005b: ldarg.0
IL_005c: ldfld class [System.Windows.Forms]System.Windows.Forms.Label VS2013DotNet4test.Form1::label1
IL_0061: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection::Add(class [System.Windows.Forms]System.Windows.Forms.Control)
IL_0066: ldarg.0
IL_0067: ldstr "Form1"
IL_006c: call instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
IL_0071: ldarg.0
IL_0072: ldc.i4.0
IL_0073: call instance void [System.Windows.Forms]System.Windows.Forms.Control::ResumeLayout(bool)
IL_0078: ldarg.0
IL_0079: call instance void [System.Windows.Forms]System.Windows.Forms.Control::PerformLayout()
IL_007e: ret
} // end of method Form1::InitializeComponent
From what I see, this shouldn't make any observable difference - it's likely simply the result of Roslyn being a smarter compiler. The value is still on the virtual stack, ready to be used when needed. There's no need to store it in a (virtual) local.
The VS2015 compiler is completely new, built from scratch, so some differences are to be expected.
Note how the VS2013 version has .maxstack
of 3, compared to 4 in VS2015 - this is the reason. VS2013's compiler simply saved one stack slot, while VS2015 saved one local slot (and a few instructions).
The value is used twice in the code, each of the compilations dealing with that in a different way: VS2013 saved the value in a local, to be retrieved when needed. VS2015 simply let the value stay on stack, and just before it's used for the first time, dup
ped it.
It might be interesting to have a look at the resulting x86 assembly from the JIT compilation - the two might even produce identical code, since most of the optimizations happen on the JIT level, not the C# compilation itself.
EDIT:
Okay, let's have a closer look at the virtual stack in the VS2015 code (I'm simplifying for clarity):
Form
as a RuntimeHandle
, pop it and push the Type
(== typeof(Form)
)Type
, push new ResourceManager
(takes the Type
argument)this
, push new Label
, pop both to store the label in the field this.label1
ResourceManager
this
again, and use it to call Control.SuspendLayout
(== this.SuspendLayout
)dup
the last value on stack (the ResourceManager
instance)this
, push this.label1
(popping this
), push "label1"
ResourceManager
, ResourceManager
, this.label1
, "label1"
ApplyResources
(which takes three arguments, so pop the last three)After all this, we still have the ResourceManager
on stack, without using any local.