OCaml调用约定:这是一个准确的总结吗?

OCaml调用约定:这是一个准确的总结吗?,ocaml,Ocaml,我一直在尝试找到OCaml调用约定,以便手动解释gdb无法解析的堆栈跟踪。不幸的是,除了一般的观察之外,似乎没有任何东西用英语写下来。例如,人们会在博客上评论OCaml在寄存器中传递了许多参数。(如果某处有英文文档,请提供链接。) 所以我一直在试图从ocamlopt的来源中找出答案。有人能证实这些猜测的准确性吗 如果我在寄存器中传递的前十个参数是正确的,那么通常不可能将这些参数恢复到函数调用中吗?在C语言中,只要我返回到正确的帧,参数仍然会被推到堆栈的某个地方。在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中传递
  • 分配指针(可能是本文中描述的次要堆)在r15中传递
  • 如果返回值是整数或指针,则返回rax;如果返回值是浮点,则返回xmm0
  • 所有寄存器都已保存?
对于调用C函数,使用标准amd64 C约定:

  • 前六个整数和指针参数在rdi、rsi、rdx、rcs、r8和r9中传递
  • 前八个浮点参数在xmm0-xmm7中传递
  • 附加参数被推送到堆栈上
  • 返回值以rax或xmm0形式传回
  • 寄存器rbx、rbp和r12-r15被调用为保存
返回地址(来自
/asmcomp/amd64/emit.mlp

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

异常(来自
/asmcomp/linearize.ml

代码
try(…body…)和(…handler…);(…rest…
线性化如下:

Lsetuptrap .body
(...handler...)
Lbranch .join
Llabel .body
Lpushtrap
(...body...)
Lpoptrap
Llabel .join
(...rest...)
然后像这样作为程序集发出(右侧的目的地):

在身体的某个地方,有一个线性化的操作码
Lraise
,它作为这个精确的组件发出:

movq %r14, %rsp
popq %r14
ret
真是太好了!我们不创建setjmp/longjmp业务,而是创建一个虚拟帧,其返回地址是异常处理程序,其唯一本地地址是前一个此类虚拟帧。
/asmcomp/amd64/proc.ml
有一条注释,将$r14称为“陷阱指针”,因此我将此虚拟帧称为陷阱帧。当我们想要引发异常时,我们将堆栈指针设置为最近的陷阱帧,将陷阱指针设置为之前的陷阱帧,然后“返回”到异常处理程序中。我打赌如果异常处理程序不能处理这个异常,它只会重新运行它


例外情况出现在%eax中。

这与其说是一个问题,不如说是一个答案!关于这个话题,我知道的一点是,我通过查看源代码学到的,就像你一样,所以不要期望进一步的精确性会比你的文章更权威

是的,我认为OCaml只对调用方保存寄存器使用专门的调用约定。这种选择的一个好处是它简化了尾部调用:当您跳过尾部调用时,您不必溢出或重新加载任何寄存器

1:对于非自尾调用,这只在没有太多参数的情况下有效,因此我们不需要溢出。如果需要堆栈分配,则该调用将转换为非尾部调用

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


我还同意“最左边的第一位”:参数按顺序通过调用
proc.ml
中的
约定进行遍历,并按偏移顺序存储在
emit.mlp中的
slot\u offset
中;它们从右到左计算,但按顺序返回,在
selectgen.ml
是中,您无法从调用中恢复参数,因为OCaml尝试尽可能地重用寄存器,因此如果在函数的剩余部分不再有用,将销毁其内容。调试器无法打印参数,只能在函数中的给定点打印仍然有效的变量,但为此,需要修改ocamlopt以转储DWARF代码以恢复值

movq %r14, %rsp
popq %r14
ret