OCaml调用约定:这是一个准确的摘要吗?

时间:2012-07-04 03:46:24

标签: ocaml

我一直在尝试找到OCaml调用约定,以便我可以手动解释gdb无法解析的堆栈跟踪。不幸的是,除了一般观察之外,似乎没有任何东西用英语写下来。例如,人们将在博客上评论OCaml在寄存器中传递了许多参数。 (如果某处有英文文档,我们非常感谢链接。)

所以我一直试图从ocamlopt来源解决它。任何人都可以确认这些猜测的准确性吗?

而且,如果我对寄存器中传递的前十个参数是对的,那么通常不可能恢复函数调用的参数吗?在C中,如果只是向后走到正确的帧,参数仍会被推到堆栈的某个地方。在OCaml中,似乎被调用者可以自由地破坏他们的呼叫者的论点。


注册分配(来自/asmcomp/amd64/proc.ml

用于调用OCaml函数,

  • 前10个整数和指针参数在寄存器rax,rbx,rdi,rsi,rdx,rcx,r8,r9,r10和r11中传递
  • 前10个浮点参数在寄存器xmm0 - xmm9
  • 中传递
  • 其他参数被压入堆栈(最左边第一个?),浮点数和整数和指针混合
  • 陷阱指针(请参阅下面的例外情况)在r14
  • 中传递
  • 分配指针(可能是本blog post中描述的次要堆)在r15中传递
  • 如果是整数或指针,则返回值在rax中传回;如果是浮点数则返回xmm0
  • 所有寄存器都是来电保存?

对于调用C函数,使用标准的amd64 C约定:

  • 前六个整数和指针参数在rdi,rsi,rdx,rcs,r8和r9中传递
  • 前八个浮点参数在xmm0 - xmm7
  • 中传递
  • 其他参数被压入堆栈
  • 返回值以rax或xmm0
  • 传回
  • 寄存器rbx,rbp和r12-r15是callee-save

返回地址(来自/asmcomp/amd64/emit.mlp

返回地址是按照amd64 C约定推入调用帧的第一个指针。 (我猜测ret指令采用这种布局。)

例外(来自/asmcomp/linearize.ml

代码try (...body...) with (...handler...); (...rest...)如下所示线性化:

Lsetuptrap .body
(...handler...)
Lbranch .join
Llabel .body
Lpushtrap
(...body...)
Lpoptrap
Llabel .join
(...rest...)

然后以像这样的程序集(右边的目的地)发出:

call .body
(...handler...)
jmp .join
.body:
pushq %r14
movq %rsp, %r14
(...body...)
popq %r14
addq %rsp, 8
.join:
(...rest...)

在身体的某个地方,有一个线性化的操作码Lraise,它会像这个精确的程序集一样被发出:

movq %r14, %rsp
popq %r14
ret

哪个真的很整洁!我们创建了一个伪帧,而不是这个setjmp / longjmp业务,它的返回地址是异常处理程序,其唯一的本地是前一个这样的虚拟帧。 /asmcomp/amd64/proc.ml有一个注释调用$ r14“陷阱指针”所以我将这个虚拟帧称为陷阱帧。当我们想要引发异常时,我们将堆栈指针设置为最近的陷阱帧,在此之前将陷阱指针设置为陷阱帧,然后“返回”到异常处理程序中。我敢打赌,如果异常处理程序无法处理此异常,它只会重新加载它。

例外是%eax。

2 个答案:

答案 0 :(得分:6)

这是一个答案,而不是一个问题!关于这个话题我知道的一点,我通过查看源代码来学习,就像你一样,所以不要指望进一步的精确性比你的帖子更具权威性。

是的,我认为OCaml仅使用专门的调用约定和调用者保存寄存器。这种选择的好处是它简化了尾调用:当你跳过尾调用¹时,你不必溢出或重新加载任何寄存器。

¹:对于非自我尾调用,这只适用于没有太多参数的情况,因此我们不需要泄漏。如果需要堆栈分配,则呼叫将变为非尾部呼叫。

请注意,调用约定仍然强烈依赖于目标体系结构。例如,在x86上,当寄存器耗尽并且在堆栈上溢出之前,会使用少量的全局变量来保留尾调用。

我也同意“leftmost-first-in”:calling_conventions中的proc.ml按{0}}顺序遍历参数,slot_offset中的emit.mlp按偏移顺序存储;它们从右向左计算,但按顺序返回selectgen.ml

答案 1 :(得分:4)

是的,你无法从调用中恢复参数,因为OCaml试图尽可能多地重用寄存器,并且如果它在函数的剩余部分中不再有用,则会破坏它们的内容。调试器无法打印参数,它们只能在函数的给定点打印仍然存在的变量,但为此,您需要修改ocamlopt以转储DWARF代码以恢复值。