OCaml内部:例外

Wan*_*eck 21 compiler-construction ocaml exception 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函数已经尝试捕获任何异常,然后将其转换为这种格式?

谢谢!

gas*_*che 44

OCaml异常处理

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

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

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

  • 字节码编译:第635-641行,两个Kpushtrap/Kpoptrap字节码环绕try..withed表达式
  • 本机编译:第254-260行,再次说明Lpushtrap/Lpoptrap表达式
  • 字节码执行对字节码PUSHTRAP(放置陷阱/处理程序), POPTRAP(删除它,非错误的情况下)和RAISE(跳转到陷阱)
  • mipsamd64 上的本机代码发射(例如)

与...比较 setjmp

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

这在C/OCaml接口中被考虑在内:从C代码调用Caml函数的常用方法是caml_callback,不会捕获OCaml-land异常; caml_callback_exn如果你愿意,你必须使用一个特定的,它设置它的陷阱处理程序保存/恢复C调用约定的被调用者保存的寄存器.见例如.amd64代码,保存寄存器然后跳转到此标签以设置异常陷阱.