Graph Traversal C and Data Structures Baojian Hua [email protected].
Exception Compiler Baojian Hua [email protected].
-
Upload
winfred-wood -
Category
Documents
-
view
221 -
download
0
Transcript of Exception Compiler Baojian Hua [email protected].
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
Scale SLP with Exceptionsfunc -> 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
Semanticsthrow:// 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”?
Two strategies
Setjmp/longjmp-based global “goto” C’s primitive exception (poor-man meth
od) Table-driven method
faster but more complex, use more space
Setjmp/longjmp#include <setjmp.h> // C standard libraryjmp_buf buf;
void f () { if (0==setjmp (buf)) g (); else;}void g () { h ();}void h () { longjmp (buf, 1);}
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!
Setjmp/longjmpjmp_buf buf;
void f () { if (0==setjmp (buf)) g (); else;}void g () { h ();}void h () { longjmp (buf, 1);}
frame f
ebxediesiebpespeip
buf
frame g
Setjmp/longjmpjmp_buf buf;
void f () { if (0==setjmp (buf)) g (); else;}void g () { h ();}void h () { longjmp (buf, 1);}
frame f
ebxediesiebpespeip
buf
frame g
frame h
movl $1, %eax
movl buf->eip, -4(buf->esp)
// restore ebx, edi, …
Setjmp/longjmpjmp_buf buf;
void f () { if (0==setjmp (buf)) g (); else;}void g () { h ();}void h () { longjmp (buf, 1);}
frame f
ebxediesiebpespeip
buf
frame g
frame h
movl $1, %eax
movl buf->eip, -4(buf->esp)
// restore ebx, edi, …
Compiling to setjmp/longjmp Basic idea:
try s1 catch s2 ==> setjmp save the context (callee-saved registers, s2’s code la
bel, 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
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
eiptopold_xsp
eiptopold_xsp
old_top
xsp
top
old_top
Compiling “throw”eiptopold_xsp
eiptopold_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.
Compiling “try…catch…”eiptopold_xsp
eiptopold_xsp
old_topxsp
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:
Compiling “try…catch…”eiptopold_xsp
eiptopold_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
Compiling “try…catch…”eiptopold_xsp
eiptopold_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
Compiling “try…catch…”eiptopold_xsp
old_topxsp
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
Compiling “try…catch…”eiptopold_xsp
eiptopold_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
Compiling “try…catch…”eiptopold_xsp
eiptopold_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
eiptopold_xsp
eiptopold_xsp
old_topxsp
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
eiptopold_xsp
eiptopold_xsp
old_top
xsptop
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
eiptopold_xsp
eiptopold_xsp
old_topxsp
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
eiptopold_xsp
eiptopold_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
eiptopold_xsp
eiptopold_xsp
old_topxsp
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
eiptopold_xsp
eiptopold_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
old_top
eiptopold_xsp
eiptopold_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
old_top
5
eiptopold_xsp
old_topxsp
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
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”
Table-driven approachgen_s (try s1 catch s2) =
.L1:
gen_s (s1)
jmp .End
.L2
gen_s (s2)
jmp .End
.L3
gen_s (throw) =
search_table(eip)
From To Handler
.L1 .L2 .L2
.L2 .L3 ?
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:
From To Handler
.L1 .L2 .L2
.L2 .L3 ?
Example #1
old_top
top
gen_s (try {throw;}
catch {print(5);}) =
.L1
search_table(eip)
jmp .End
.L2:
push 5
call print
jmp .End
.End:
From To Handler
.L1 .L2 .L2
.L2 .L3 ?
Example #2
old_top
top
gen_s (try {f();}
catch {print(5);}) =
.L1
f()
jmp .End
.L2:
push 5
call print
jmp .End
.End:
From To Handler
.L1 .L2 .L2
.L2 .L3 ?
Example #3
f(){
g();
print(6);
}
g(){
throw;
}
old_top
old_top
Ooops!! Current eip is not in the table!!It’s time to unwind the stack!
old_top
top
gen_s (try {f();}
catch {print(5);}) =
.L1
f()
jmp .End
.L2:
push 5
call print
jmp .End
.End:
From To Handler
.L1 .L2 .L2
.L2 .L3 ?
Example #3
f(){
g();
print(6);
}
g(){
throw;
}
old_top
old_top
Ooops!! Current eip is not in the table!!It’s time to unwind the stack!Saved eip still not in stack
old_top
top
gen_s (try {f();}
catch {print(5);}) =
.L1
f()
jmp .End
.L2:
push 5
call print
jmp .End
.End:
From To Handler
.L1 .L2 .L2
.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
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
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++ u
se this scheme Disadvantage:
exception table takes extra space
try…finally…
A little bit tricky Sun’s old version JDK compiler uses a f
ancy 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