On Nov 26, 2007, at 23:40, Chris Lattner wrote:
> On Nov 26, 2007, at 11:30 AM, Gordon Henriksen wrote:
>
>>
>> 1. The ocaml exception model is quite unique; emulating it seems
>> unlikely. DWARF exceptions are a suitable but incompatible
>> replacement.
>
> Can you explain how the ocaml exception model works?
Sure. With ocamlopt's model, try has cost only slightly higher than a
function call and raise has cost only slightly higher than return. I'm
not concerned with the bytecode interpreter and compiler, ocamlc and
ocamlrun.
As Daniel Berlin pointed out on IRC, the language model is trivial. It
has just three exception-handling primitives:
matching ::= pattern ‘->’ expr ( ‘|’ pattern ‘->’ expr )*
raise expr
try expr with matching
exception id ( tuple-type-expr )?
exception declares an exception type. For the purposes of this
discussion, exception values are just heap pointers. raise and with
only accept expressions of an exception type.
raise unwinds the stack, submitting expr to the first matching pattern
in the nearest enclosing try-with. The runtime provides a catch-all at
startup which terminates the program. The matching expression is
sufficient to implement finally (x -> expr) and catch-all (x -> expr;
raise x). Each finally expression requires its own try-with expression.
The codegen for raise is simple. It just reads a saved return address
from the caml_exception_pointer global and returns through several
stack frames in one go. The expression raise expr is compiled as such:
; Store the exception value in the return register.
$r1 = ...
; Change the stack pointer.
load $sp <- 0(caml_exception_pointer)
; The stack now contains:
; ... rest of stack ...
; return address
; old caml_exception_pointer
; Restore caml_exception_pointer.
pop 0(caml_exception_pointer)
; Jump straight to the nearest landing pad.
ret
The try-with expression is where the trickery lies. The expression try
body with pattern1 -> catch1 | patternN -> catchN is compiled to:
; 'Call' a label within the function.
call try_block
; This is the landing pad. The exception is in the return register.
; Try to match a handler.
$r2 = $1 matches pattern1
branch to handler1_block if $r2
$r2 = $1 matches patternN
branch to handlerN_block if $r2
; If no handler matched, re-raise the exception.
load $sp <- 0(caml_exception_pointer)
pop 0(caml_exception_pointer)
ret
handler1_block:
$r1 = catch1
jump to after_block
handlerN_block:
$r1 = catchN
jump to after_block
try_block:
; Save caml_exception_pointer on the stack.
push 0(caml_exception_pointer)
; The top of the stack is now what raise expects:
; ... rest of stack ...
; return address
; old caml_exception_pointer
; Save $sp into caml_exception_poointer.
store $sp -> 0(caml_exception_pointer)
; Generate try body here.
$r1 = body ; n.b.: use increased stack offsets to access locals.
; Restore the saved value of caml_exception_pointer.
pop 0(caml_exception_pointer)
; Resume to the normal flow of execution by undoing the stack
; adjustments made by call and falling through. A normal 'ret'
; would jump into the exception handler, which we don't want!
pop
after_block:
; Either the exception was handled, or none was raised.
— Gordon
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20071127/37e4cfc1/attachment.html>