Download presentation
Presentation is loading. Please wait.
1
Exception Compiler Baojian Hua bjhua@ustc.edu.cn
2
Exception Exception is for error-handling Invalid input Invalid resource state file not exists, network error, … error execution condition divide-by-zero, … In real production code, error-handling code may be a large part 30%-50% or more
3
Scale SLP with Exceptions func -> id(args){ s } s -> x := e | print (e) | return e | throw | try s catch s // Example: try {x := 3} catch { print(5);} try {throw; print(4);} catch 5
4
Semantics throw: // abort the current execution, and notify the // system some bad things happen! try s1 catch s2: // first execute s1, // 1. if s1 runs smoothly, then // the statement finished executing; // 2. else, some exception is thrown in s1, // then run s2. // Q: what about s2 “throw”?
5
Two strategies Setjmp/longjmp-based global “ goto ” C ’ s primitive exception (poor-man method) Table-driven method faster but more complex, use more space
6
Setjmp/longjmp #include // C standard library jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); }
7
Setjmp/longjmp // What’s going on under the hood? struct context { int ebx; int edi; int esi; int ebp; int esp; int eip; }; typedef struct context jmp_buf[1]; Callee-saved registers! Saved pc!
8
Setjmp/longjmp jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f ebx edi esi ebp esp eip buf frame g
9
Setjmp/longjmp jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f ebx edi esi ebp esp eip buf frame g frame h movl $1, %eax movl buf->eip, -4(buf->esp) // restore ebx, edi, …
10
Setjmp/longjmp jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f ebx edi esi ebp esp eip buf frame g frame h movl $1, %eax movl buf->eip, -4(buf->esp) // restore ebx, edi, …
11
Compiling to setjmp/longjmp Basic idea: try s1 catch s2 ==> setjmp save the context (callee-saved registers, s2 ’ s code label, etc.) the context is also called a handler throw ==> longjmp pop the topmost handler, restore machine states from the handler and jump to the handler ’ s saved pc “ Try ” may nest, so all handlers should be organized as a stack
12
Machine configuration M = (C, S, X): C: code heap S: operand stack pointed by “ top ” as we did for the stack machine X: exception handler stack pointed by “ xsp ” “ top ” points to top of some frame “ eip ” points to code heap eip top old_xsp eip top old_xsp old_top xsp top old_top
13
Compiling “ throw ” eip top old_xsp eip top old_xsp old_top xsp top old_top gen_s (throw) = top = xsp->top jmp xsp->eip // Essentially the same as // “longjmp”. // To simplify things, we omit // the callee-saved regs here.
14
Compiling “ try … catch …” eip top old_xsp eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End:
15
Compiling “ try … catch …” eip top old_xsp eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End: if s1 does NOT throw
16
Compiling “ try … catch …” eip top old_xsp eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End: if s1 does NOT throw
17
Compiling “ try … catch …” eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End: if s1 does NOT throw
18
Compiling “ try … catch …” eip top old_xsp eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End: if s1 does throw! gen_s (throw) = top = xsp->top jmp xsp->eip
19
Compiling “ try … catch …” eip top old_xsp eip top old_xsp old_top xsp top gen_s (try s1 catch s2) = push a new handler xsp->eip =.Handler xsp->top = top gen_s (s1) pop a handler jmp.End.Handler: pop a handler gen_s (s2) jmp.End.End: if s1 does throw! gen_s (throw) = top = xsp->top jmp xsp->eip
20
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {x:=3;} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top push 3 store x pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: 3 Example #1
21
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {x:=3;} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top push 3 store x pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: Example #1
22
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {throw;} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top top = xsp->top jmp xsp->eip pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: gen_s (throw) = top = xsp->top jmp xsp->eip Example #2
23
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {throw;} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top top = xsp->top jmp xsp->eip pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: gen_s (throw) = top = xsp->top jmp xsp->eip Example #2 5
24
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {f();} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top f() pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: Example #3 f(){ g(); print(6); } g(){ throw; } old_top
25
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {f();} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top f() pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: Example #3 f(){ g(); print(6); } g(){ throw; } old_top
26
eip top old_xsp eip top old_xsp old_top xsp top gen_s (try {f();} catch {print(5);}) = push a new handler xsp->eip =.Handler xsp->top = top f() pop a handler jmp.End.Handler: pop a handler push 5 call print jmp.End.End: Example #3 f(){ g(); print(6); } g(){ throw; } old_top 5
27
eip top old_xsp old_top xsp top // What about a wild // “throw”? main(){ throw; } gen_f (main(){throw;}) = main: top = xsp->top jmp xsp->eip // Where does xsp->eip // point to? // Do the assigned homework! Example #4
28
Moral Relatively easy to implement Many C++ compilers use this scheme, e.g. VC from MS (the so-called SEH) the exception handler stack can be combined with operand stack (and further with the call stack) Disadvantage: performance penalty at each “ try ” even if the try body does not “ throw ” an exception slogan: “ pay as you go ”
29
Table-driven approach gen_s (try s1 catch s2) =.L1: gen_s (s1) jmp.End.L2 gen_s (s2) jmp.End.L3 gen_s (throw) = search_table(eip) FromToHandler.L1.L2.L3?
30
old_top top gen_s (try {x:=3;} catch {print(5);}) =.L1 push 3 store x jmp.End.L2: push 5 call print jmp.End.End: FromToHandler.L1.L2.L3? Example #1
31
old_top top gen_s (try {throw;} catch {print(5);}) =.L1 search_table(eip) jmp.End.L2: push 5 call print jmp.End.End: FromToHandler.L1.L2.L3? Example #2
32
old_top top gen_s (try {f();} catch {print(5);}) =.L1 f() jmp.End.L2: push 5 call print jmp.End.End: FromToHandler.L1.L2.L3? Example #3 f(){ g(); print(6); } g(){ throw; } old_top Ooops!! Current eip is not in the table!! It ’ s time to unwind the stack!
33
old_top top gen_s (try {f();} catch {print(5);}) =.L1 f() jmp.End.L2: push 5 call print jmp.End.End: FromToHandler.L1.L2.L3? Example #3 f(){ g(); print(6); } g(){ throw; } old_top Ooops!! Current eip is not in the table!! It ’ s time to unwind the stack! Saved eip still not in stack
34
old_top top gen_s (try {f();} catch {print(5);}) =.L1 f() jmp.End.L2: push 5 call print jmp.End.End: FromToHandler.L1.L2.L3? Example #3 f(){ g(); print(6); } g(){ throw; } old_top Ooops!! Current eip is not in the table!! It ’ s time to unwind the stack! Saved eip still not in stack
35
Moral Compilers produce an exception table, which is referred to when an exception is raised based on the value of current and saved “ eip ” s Normal execution can proceed at full speed
36
Moral Table-driven scheme has no cost with normal execution exceptions are exceptional, pay as you go both Sun ’ s HotSpot JVM and GNU g++ use this scheme Disadvantage: exception table takes extra space
37
try … finally … A little bit tricky Sun ’ s old version JDK compiler uses a fancy but wrong technique---subroutine has been fixed in JDK version after 1.4 Read the assigned paper: The Costs and Benefits of Java Bytecode Subroutines
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.