exception - OCaml 内部结构 : Exceptions

标签 exception compiler-construction ocaml internals setjmp

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

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

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

谢谢!

最佳答案

OCaml 异常处理

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

¹:或全局,在寄存器不足的架构上

您可以在代码中亲自查看:

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

setjmp比较

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

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

关于exception - OCaml 内部结构 : Exceptions,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8564025/

相关文章:

objective-c - 关于 Objective-C 中没有主体的 "for"循环的奇怪编译器优化/行为

algorithm - OCaml递归解决棋盘拼图上骑士最短路径的堆栈溢出

ocaml - 使用 List.fold_right 将数字插入排序列表

c++ - NULL 与抛出和性能

c++ - close() 抛出什么异常类型?

java - 如何避免自定义 Java 异常类中的重复

c - PIC32 取消引用指针可能是编译器错误

compiler-construction - 如何在多 channel 编译器中正确重用符号表

ocaml - 如何使用 Format.fprintf 函数返回 pretty-print 字符串?

c# - Catch 提供的 URI 方案 'http' 无效;预期 'https' 错误