1 / 37

Exception

Exception. Compiler Baojian Hua bjhua@ustc.edu.cn. 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

karik
Download Presentation

Exception

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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 <setjmp.h> // 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 buf jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f frame g

  9. Setjmp/longjmp buf jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f frame g frame h movl $1, %eax movl buf->eip, -4(buf->esp) // restore ebx, edi, …

  10. Setjmp/longjmp buf jmp_buf buf; void f () { if (0==setjmp (buf)) g (); else; } void g () { h (); } void h () { longjmp (buf, 1); } frame f 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 old_top xsp old_top top

  13. Compiling “throw” gen_s (throw) = top = xsp->top jmp xsp->eip // Essentially the same as // “longjmp”. // To simplify things, we omit // the callee-saved regs here. old_top xsp old_top top

  14. Compiling “try…catch…” 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: old_top xsp top

  15. Compiling “try…catch…” 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 old_top top xsp

  16. Compiling “try…catch…” 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 old_top top xsp

  17. Compiling “try…catch…” 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 old_top xsp top

  18. Compiling “try…catch…” 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! old_top top xsp gen_s (throw) = top = xsp->top jmp xsp->eip

  19. Compiling “try…catch…” 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! old_top top xsp gen_s (throw) = top = xsp->top jmp xsp->eip

  20. Example #1 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 old_top xsp top

  21. Example #1 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: old_top top xsp

  22. Example #2 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: old_top xsp top gen_s (throw) = top = xsp->top jmp xsp->eip

  23. Example #2 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: 5 old_top top xsp gen_s (throw) = top = xsp->top jmp xsp->eip

  24. Example #3 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: old_top xsp f(){ g(); print(6); } g(){ throw; } top old_top

  25. Example #3 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: old_top f(){ g(); print(6); } g(){ throw; } old_top xsp top old_top

  26. Example #3 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: 5 old_top f(){ g(); print(6); } g(){ throw; } old_top xsp old_top top

  27. Example #4 // 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! old_top xsp top

  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)

  30. Example #1 gen_s (try {x:=3;} catch {print(5);}) = .L1 push 3 store x jmp .End .L2: push 5 call print jmp .End .End: old_top top

  31. Example #2 gen_s (try {throw;} catch {print(5);}) = .L1 search_table(eip) jmp .End .L2: push 5 call print jmp .End .End: old_top top

  32. Example #3 f(){ g(); print(6); } g(){ throw; } gen_s (try {f();} catch {print(5);}) = .L1 f() jmp .End .L2: push 5 call print jmp .End .End: Ooops!! Current eip is not in the table!! old_top It’s time to unwind the stack! top old_top old_top

  33. Example #3 f(){ g(); print(6); } g(){ throw; } gen_s (try {f();} catch {print(5);}) = .L1 f() jmp .End .L2: push 5 call print jmp .End .End: Ooops!! Current eip is not in the table!! old_top It’s time to unwind the stack! Saved eip still not in stack old_top old_top top

  34. Example #3 f(){ g(); print(6); } g(){ throw; } gen_s (try {f();} catch {print(5);}) = .L1 f() jmp .End .L2: push 5 call print jmp .End .End: Ooops!! Current eip is not in the table!! old_top It’s time to unwind the stack! Saved eip still not in stack old_top top

  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

More Related