OCaml内部:例外

时间:2011-12-19 16:06:52

标签: exception compiler-construction ocaml internals longjmp

我很想知道如何在OCaml运行时处理异常以使它们如此轻量级。他们使用setjmp / longjmp还是在每个函数中返回一个特殊值,然后传播它?

在我看来,longjmp会对系统造成一点压力,但只有在引发异常时,检查每个函数的返回值才需要在调用函数后检查每个值,这似乎是我会进行大量的检查和跳转,看起来它会表现最差。

通过查看OCaml如何与C(http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html#toc142)接口,并查看callback.h,似乎通过使用对象的内存对齐来标记异常(#define Is_exception_result(v)((( v)& 3)== 2))。这似乎表明它的实现不使用longjmp并在每次函数调用后检查每个函数结果。是吗?或者C函数已经尝试捕获任何异常,然后将其转换为这种格式?

谢谢!

1 个答案:

答案 0 :(得分:42)

OCaml异常处理

它不使用setjmp/longjmp。在评估try <expr> with <handle>时,会在堆栈上放置一个“陷阱”,其中包含有关处理程序的信息。最顶层陷阱的地址保存在寄存器¹中,当你加注时,它会直接跳转到这个陷阱,一次解除几个堆栈帧(这比检查每个返回码更好)。陷阱还存储先前陷阱的地址,该陷阱在加速时在寄存器中恢复。

¹:或者是一个没有足够寄存器的全局架构

您可以在代码中看到自己:

  • bytecode compilation:第635-641行,Kpushtrap/Kpoptrap ed表达式周围有两个try..with字节码
  • native compilation:第254-260行,在表达式
  • 周围再次说明Lpushtrap/Lpoptrap
  • bytecode execution用于字节码PUSHTRAP(放置陷阱/处理程序),POPTRAP(删除它,非错误情况)和RAISE(跳转到陷阱)
  • 本机代码发送on mipson amd64(例如)

setjmp

的比较

Ocaml使用非标准调用约定,很少或没有被调用者保存的寄存器,这使得这(和尾递归)有效。我想(但我不是专家)这就是为什么C longjmp/setjmp在大多数架构上效率不高的原因。例如,参见this x86_64 setjmp implementation,它看起来与先前的陷阱机制和callee-registers save完全相同。

C/OCaml interface中考虑了这一点:从C代码caml_callback调用Caml函数的常用方法不会捕获OCaml-land异常;如果您愿意,您必须使用特定的caml_callback_exn,它设置其陷阱处理程序保存/恢复C调用约定的被调用者保存的寄存器。见例如。 the amd64 code,保存寄存器,然后跳转到this label以设置异常陷阱。