OCaml 内部结构:异常
我很想知道 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 函数已经尝试捕获任何异常,然后将其转换为这种格式?
谢谢你!
I'm curious to know how exceptions are dealt with in OCaml runtime to make them so lightweight. Do they use setjmp/longjmp or do they return a special value in each function, and propagate it?
It seems to me that longjmp would put a little strain on the system, but only when an exception is raised, while checking for each function return value would need to check for every and each value after calling a function, which seems to me would put a lot of checks and jumps, and it seems it would perform worst.
By looking at how OCaml interfaces with C ( http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html#toc142 ), and looking at callback.h, it seems that an exception is tagged by using the memory alignment of objects ( #define Is_exception_result(v) (((v) & 3) == 2) ). This seems to indicate that its implementation doesn't use longjmp and checks each function result after each function call. Is that it? Or the C function already tries to catch any exception, and then converts it to this format?
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
OCaml 异常处理
它不使用
setjmp/longjmp
。当try时当
被评估时,一个“陷阱”被放置在堆栈上,其中包含有关处理程序的信息。最顶层陷阱的地址保存在寄存器中,当您引发时,它会直接跳转到该陷阱,一次性展开多个堆栈帧(这比检查每个返回代码更好)。陷阱还存储前一个陷阱的地址,该地址在上升时间恢复到寄存器中。¹:或全局,在寄存器不足的架构上
您可以在代码中亲自查看:
Kpushtrap/Kpoptrap
字节码围绕try..with
ed 表达式PUSHTRAP
(放置陷阱/处理程序),POPTRAP
(删除它,非错误情况)和RAISE
(跳转到陷阱)与
setjmp
的比较Ocaml 使用非标准调用约定,很少或没有被调用者保存寄存器,这使得这个(和尾递归)变得高效。我想(但我不是专家)这就是 C
longjmp/setjmp
在大多数架构上效率不高的原因。请参阅示例 此 x86_64 setjmp 实现 看起来与之前的捕获机制加上被调用者寄存器保存完全一样。C/OCaml 接口 中考虑了这一点:调用C 代码中的 Caml 函数
caml_callback
不会捕获 OCaml 领域的异常;如果您愿意,您必须使用特定的 caml_callback_exn 来设置其陷阱处理程序并保存/恢复 C 调用约定的被调用者保存的寄存器。参见例如。 amd64 代码,保存寄存器然后跳转到此标签进行设置异常陷阱。OCaml exception handling
It doesn't use
setjmp/longjmp
. When atry <expr> with <handle>
is evaluated, a "trap" is placed on the stack, that contains information about the handler. The address of the topmost trap is kept in a register¹, and when you raise, it jumps directly to this trap, unwinding several stack frames in one go (this is better than checking each return code). A trap also stores the address of the previous trap, which is restored in the register at raise time.¹: or a global, on architectures with not enough registers
You can see for yourself in the code:
Kpushtrap/Kpoptrap
bytecodes surround thetry..with
ed expressionLpushtrap/Lpoptrap
around the expressionPUSHTRAP
(places the trap/handler),POPTRAP
(remove it, non-error case) andRAISE
(jump to the trap)Comparison with
setjmp
Ocaml uses a non-standard calling convention with few or no callee-saved registers, which makes this (and tail-recursion) efficient. I suppose (but I'm no expert) that's the reason why C
longjmp/setjmp
isn't as efficient on most architectures. See for example this x86_64 setjmp implementation that looks exactly like the previous trapping mechanism plus callee-registers save.This is taken into account in the C/OCaml interface: the usual way to call a Caml function from C code,
caml_callback
, doesn't catch OCaml-land exceptions; you have to use a specificcaml_callback_exn
if you wish to, which setups its trap handler and saves/restores callee-saved registers of the C calling convention. See eg. the amd64 code, which saves the registers then jump to this label to setup the exception trap.