How to understand JITed code for "use" with exception handling in C #

I wrote a very simple class in C #:

class DisposableClass : IDisposable { public void Dispose() { } } static void UsingClass() { // line 31 using (var dc = new DisposableClass()) { // line 32 DoSomething(dc); // line 33 } // line 34 } // line 35 

I reset my own code after JIT using WinDBG:

 0:000> !u 000007fe87d30120 Normal JIT generated code SimpleConsole.Program.UsingClass() Begin 000007fe87d30120, size 80 c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: >>> 000007fe`87d30120 55 push rbp 000007fe`87d30121 4883ec30 sub rsp,30h 000007fe`87d30125 488d6c2420 lea rbp,[rsp+20h] 000007fe`87d3012a 48896500 mov qword ptr [rbp],rsp 000007fe`87d3012e 48c7450800000000 mov qword ptr [rbp+8],0 000007fe`87d30136 488d0d6b47eeff lea rcx,[000007fe`87c148a8] 000007fe`87d3013d e8fe24665f call clr+0x2640 (000007fe`e7392640) (JitHelp: CORINFO_HELP_NEWSFAST) // new DisposableClass() 000007fe`87d30142 48894508 mov qword ptr [rbp+8],rax c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 33: 000007fe`87d30146 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d3014a e8d1beeeff call 000007fe`87c1c020 (SimpleConsole.Program.DoSomething(System.Object), mdToken: 0000000006000012) 000007fe`87d3014f 90 nop 000007fe`87d30150 90 nop c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 000007fe`87d30151 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d30155 4c8d1dc4feeeff lea r11,[000007fe`87c20020] 000007fe`87d3015c ff15befeeeff call qword ptr [000007fe`87c20020] // Call Dispose() 000007fe`87d30162 90 nop 000007fe`87d30163 488d6510 lea rsp,[rbp+10h] 000007fe`87d30167 5d pop rbp 000007fe`87d30168 c3 ret // I could understand the code above (without exception thrown). c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: 000007fe`87d30169 55 push rbp 000007fe`87d3016a 4883ec30 sub rsp,30h 000007fe`87d3016e 488b6920 mov rbp,qword ptr [rcx+20h] 000007fe`87d30172 48896c2420 mov qword ptr [rsp+20h],rbp 000007fe`87d30177 488d6d20 lea rbp,[rbp+20h] c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 000007fe`87d3017b 48837d0800 cmp qword ptr [rbp+8],0 000007fe`87d30180 7417 je 000007fe`87d30199 000007fe`87d30182 488d1597feeeff lea rdx,[000007fe`87c20020] 000007fe`87d30189 488b4508 mov rax,qword ptr [rbp+8] 000007fe`87d3018d 803800 cmp byte ptr [rax],0 000007fe`87d30190 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d30194 4c8bda mov r11,rdx 000007fe`87d30197 ff12 call qword ptr [rdx] 000007fe`87d30199 90 nop 000007fe`87d3019a 4883c430 add rsp,30h 000007fe`87d3019e 5d pop rbp 000007fe`87d3019f c3 ret 

I could understand the code without exception (comment above), but how does the code work when an exception is thrown? How does the code go into the code below the comment?

Update:

Some people think we should start with IL, so I pasted the code below:

 .method private hidebysig static void UsingClass () cil managed noinlining { // Method begins at RVA 0x23bc // Code size 25 (0x19) .maxstack 1 .locals init ( [0] class SimpleConsole.DisposableClass dc ) IL_0000: newobj instance void SimpleConsole.DisposableClass::.ctor() IL_0005: stloc.0 .try { IL_0006: ldloc.0 IL_0007: call void SimpleConsole.Program::DoSomething(object) IL_000c: leave.s IL_0018 } // end .try finally { IL_000e: ldloc.0 IL_000f: brfalse.s IL_0017 IL_0011: ldloc.0 IL_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0017: endfinally } // end handler IL_0018: ret } // end of method Program::UsingClass 

But I don't think this helps, since IL almost keeps everything here in C # like a complete try...finally . I want to understand how native code works here.

+4
source share
2 answers

Jitter does a lot more than ever seen in the Disassemble window. First, it generates a table that describes the lifetime and storage of local variables. Very important for the garbage collector, it needs this table to find references to objects.

And it generates an unwind table for exceptions. Which has a very desirable property, it makes the try clause free. There is zero cost when writing code with exception handling; no code is required to enter a try block. Thus, you do not see anything in disassembly. There is no easy way to find this table from the debugger. Here's a pretty decent description .

+6
source

In fact, you look at the fact that exception handling has too little overhead in .NET when exceptions are not thrown. In the case of an exception, the structure uses various algorithms outside the normal path of the code stream (the specific ones depend on what type of exception was thrown), which will either call or explicitly point to the exception handling block for the method.

+5
source

All Articles