Compiling exceptions correctly

COMPILING EXCEPTIONS
CORRECTLY
Graham Hutton and Joel Wright
University of Nottingham
What Is An Exception?
An event within a computation that causes
termination in a non-standard way.
Examples:
 Division by zero;
 Stack overflow;
 Null pointer.
1
This Talk
 Most modern languages support programming
with exceptions, e.g. using throw and catch;
 The compilation of such exception primitives is
traditionally viewed as an advanced topic;
 We give a simple explanation and verification,
using elementary functional techniques.
2
Step 1 - Arithmetic Expressions
Syntax:
data Expr = Val Int | Add Expr Expr
Semantics:
eval
:: Expr  Int
eval (Val n)
= n
eval (Add x y) = eval x + eval y
3
Virtual machine:
type Stack = [Int]
data Op
= PUSH Int | ADD
type Code
= [Op]
Compiler:
comp
comp (Val n)
:: Expr  Code
= [PUSH n]
comp (Add x y) = comp x ++ comp y ++ [ADD]
4
Compiler Correctness
Expr
eval
comp
Code
Int
[]
exec []
Stack
Theorem:
exec s (comp e) = eval e : s
5
Proof: by induction, using a distribution lemma:
exec s (xs ++ ys)
=
exec (exec s xs) ys
However, we can avoid this lemma and shorten the
proof by 60% by generalising the theorem:
exec s (comp e ++ ops)
=
exec (eval e : s) ops
6
Step 2 - Adding Exceptions
Syntax:
data Expr = ••• | Throw | Catch Expr Expr
Semantics:
eval
eval
eval
eval
eval
::
(Val n)
=
(Throw)
=
(Add x y)
=
(Catch x h) =
Expr  Maybe Int
Just n
Nothing
eval x  eval y
eval x  eval h
7
Examples:
Add (Val 1) (Val 2)
Add Throw (Val 2)
Catch (Val 1) (Val 2)
Catch Throw (Val 2)
eval
eval
eval
eval
Just 3
Nothing
Just 1
Just 2
8
Virtual machine:
data Op = ••• | THROW | MARK Code | UNMARK
type Stack = [Item]
data Item
= VAL Int | HAN Code
Compiler:
comp (Throw)
= [THROW]
comp (Catch x h) =
[MARK (comp h)] ++ comp x ++ [UNMARK]
9
How Is THROW Executed?
Informally, we must:
 Unwind the stack seeking a handler;
 Execute the first handler found, if any;
 Skip to the next part of the computation.
10
Implementation:
unwind
::
unwind []
ops =
unwind (VAL n : s) ops =
unwind (HAN h : s) ops =
Stack  Code  Stack
[]
unwind s ops
exec s (h ++ skip ops)
skip
skip
skip
skip
skip
Code  Code
[]
ops
skip (skip ops)
skip ops
[]
(UNMARK : ops)
(MARK h : ops)
(op
: ops)
::
=
=
=
=
11
Example
1 + (catch (2 + throw) 3)
Code
Stack
12
Example
1 + (catch (2 + throw) 3)
Code
Stack
PUSH 1
MARK [PUSH 3]
PUSH 2
THROW
ADD
UNMARK
ADD
13
Example
1 + (catch (2 + throw) 3)
Code
MARK [PUSH 3]
PUSH 2
THROW
ADD
UNMARK
ADD
Stack
VAL 1
14
Example
1 + (catch (2 + throw) 3)
Code
PUSH 2
THROW
ADD
UNMARK
ADD
Stack
HAN [PUSH 3]
VAL 1
15
Example
1 + (catch (2 + throw) 3)
Code
THROW
ADD
UNMARK
ADD
Stack
VAL 2
HAN [PUSH 3]
VAL 1
16
Example
1 + (catch (2 + throw) 3)
Code
THROW
ADD
UNMARK
ADD
Stack
HAN [PUSH 3]
VAL 1
17
Example
1 + (catch (2 + throw) 3)
Code
PUSH 3
THROW
ADD
UNMARK
ADD
Stack
VAL 1
18
Example
1 + (catch (2 + throw) 3)
Code
THROW
ADD
UNMARK
ADD
Stack
VAL 3
VAL 1
19
Example
1 + (catch (2 + throw) 3)
Code
ADD
UNMARK
ADD
Stack
VAL 3
VAL 1
20
Example
1 + (catch (2 + throw) 3)
Code
UNMARK
ADD
Stack
VAL 3
VAL 1
21
Example
1 + (catch (2 + throw) 3)
Code
ADD
Stack
VAL 3
VAL 1
22
Example
1 + (catch (2 + throw) 3)
Code
Stack
VAL 4
23
Compiler Correctness
Expr
eval
comp
Code
Maybe Int
conv
exec []
Stack
where
conv Nothing = []
conv (Just n) = [VAL n]
24
As previously, we generalise to an arbitrary initial
stack and arbitrary additional code.
Theorem:
exec s (comp e ++ ops)
=
exec s (trans (eval e) : ops)
where
trans
:: Maybe Int  Op
trans Nothing = THROW
trans (Just n) = PUSH n
25
Proof: by induction, using a skipping lemma:
skip (comp e ++ ops) = skip ops
Notes:
 The proof is 3.5 pages of simple calculation;
 The quickcheck tool was very useful as an aid
to simplifying the definitions and results.
26
Step 3 - Adding Jumps
Basic idea:
Catch x h
is now compiled to
See the paper for
further details.
MARK a
code for x
UNMARK
JUMP b
a: code for h
b: remaining code
27
Summary
 Explanation and verification of the compilation of
exceptions using stack unwinding.
 Stepwise development to aid understanding:
1 - Arithmetic expressions;
2 - Adding exceptions;
3 - Adding jumps.
28
Further Work
 Mechanical verification;
 Modular compilers;
 Calculating the compiler;
 Generalising the language;
 Compiler optimisations.
29