0

I'm trying to disassemble my C# code and then debug it on assembly language level. Let's say we have a simple C# method:

var a = 1235;
var b = ++a;
var c = ++b;

Console.WriteLine("test");
Console.ReadKey();

I've found two different ways how to get an assembly code. The first one is to start C# code debugging in VS and then open Disassembly window. Here we have the following code.

first method assembly

Everything is OK and assembly code is pretty much simple and short but the problem is that the logic of this assembly code differs from the logic of the IL code generated by ildasm.

cil code

So here is the second way. We can compile C# code, use ildasm to get the IL code from PE file and then use ilasm to generate PE file back. Now we have the following assembly code.

second method code

As you can see this assembly code is more like the IL code but it contains much more instructions and it is more complicated.

AFAIK C# compiles to CIL code and then to an assembly code in both cases. But it seems to be that in the first way it just compiles to an assembly code.

So the question is why the assembly code of the first method differs from the IL code? And why the assembly code of the first method differs from the assembly code of the second method?

khrabrovart
  • 169
  • 1
  • 9
  • 3
    The CLR is a virtual machine, hence the difference in the code. Similar to Java's JVM. – t0mm13b Jun 16 '17 at 19:30
  • 4
    It's debug mode code (you likely have "Suppress JIT optimization on module load" turned on) so not very interesting, but it is curious that it's different depending on whether it has gone through an ildasm/ilasm round trip. – harold Jun 16 '17 at 19:34
  • 6
    It is not a "problem", it is a feature. Looking at the machine code is only sensible when you use the Release build and turn off the Suppress JIT optimization option. – Hans Passant Jun 16 '17 at 19:36

1 Answers1

3

The JIT is able to re-order and merge machine instructions as an optomisation, but will try to avoid moving the effects across sequence points provided by the pdb. The compiler generally generates one sequence point per line of code, since you generally step through it one line at a time.

While C# will often generate multiple IL instructions per line of code, ilasm is given each instruction explicitly and so generates more sequence points, leaving less room for JIT optimizations.

Brian Reichle
  • 2,798
  • 2
  • 30
  • 34
  • This answer is not very accurate, the PDB file most certainly has nothing to do with sequence points. Nor does "line of code". Code hoisting and subexpression elimination are standard optimizer techniques that the JIT optimizer knows to apply as well. Backgrounder post [is here](https://stackoverflow.com/a/4045073/17034). – Hans Passant Jun 25 '17 at 07:35
  • 1
    @HansPassant, I think you might be confusing the PDB's concept of sequence points with the C++ concept with the same name. The pdb uses the term "Sequence Point" to describe the [IL <-> source mappings](https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/diagnostics/isymunmanagedmethod-getsequencepoints-method). The idea is that a debugger will most likely want to stop on the start of a "sequence point" and so the JIT will try to make things look consistent at those points (unless told otherwise with the DebuggableAttribute flag described in the article I linked to in the answer.) – Brian Reichle Jun 25 '17 at 21:56
  • Thanks for the lesson, never saw that usage of the term before. Future readers might want to know that the optimizer makes no attempt at keeping the PDB info relevant. With the default project configuration, the PDB doesn't even contain any line number info at all. – Hans Passant Jun 25 '17 at 22:02