2

When I compile the following code as debug...

public class MyClass
{
    private int myField;

    public int MyProperty
    {
        get { return myField; }
        set { myField = value; }
    }
}

...strange bytecode with seemingly useless instructions is produced by the compiler. For instance see what is generated for the getter of property MyProperty (disassembled with ildasm.exe):

.method public hidebysig specialname instance int32 
        get_MyProperty() cil managed
{
    // Code size       12 (0xc)
    .maxstack  1
    .locals init ([0] int32 CS$1$0000)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldfld      int32 MSILTest.MyClass::myField
    IL_0007:  stloc.0
    IL_0008:  br.s       IL_000a
    IL_000a:  ldloc.0
    IL_000b:  ret
} // end of method MyClass::get_MyProperty

Specifically, what is the nop at IL_0000 doing there? And why does the compiler generate this useless br.s instruction at IL_0008 literally out of nowhere? Why does it create the temporary local variable CS$1$0000?

For a release configuration the instruction set is generated as expected though:

IL_0000:  ldarg.0
IL_0001:  ldfld      int32 MSILTest.MyClass::myField
IL_0006:  ret

EDIT

I think I've found an answer to the question why the branch and the temporary local variable is there in a different question: it's probably to easy setting break points during debugging. So the question that remains is why the nop instruction is genereated.

Community
  • 1
  • 1
Good Night Nerd Pride
  • 8,245
  • 4
  • 49
  • 65
  • 2
    Non-optimized code is not optimized. The NOP is there to be a target of a debugger breakpoint. Removing redundant loads and stores produced by a recursive-decent parser requires a peephole optimizer. Eliminating unnecessary locals is also an optimizer job. The jitter itself can tackle these optimizations jobs without help from the compiler btw. – Hans Passant Jan 13 '16 at 11:20
  • 1
    See also [Subtleties of C# IL codegen](https://blogs.msdn.microsoft.com/ericlippert/2007/08/17/subtleties-of-c-il-codegen/), the first Google hit for "C# debug nop": _"With optimizations off, the C# compiler emits no-op IL instructions all over the place. With debug info on and optimizations off, some of those no-ops will be there to be targets of breakpoints for statements or fragments of expressions which would otherwise be hard to put a breakpoint on"_. – CodeCaster Jan 13 '16 at 11:22

1 Answers1

3

The compiler creates these opcodes to provide a better debugging experience. At the start of each of your c# lines the compiler will introduce a nop so the debugger can break there. The branch and the temporary local variable are emitted to let you analyse the functions return value.

thehennyy
  • 4,020
  • 1
  • 22
  • 31