使用caml_c_call直接从OCaml调用C /汇编函数

Pas*_*uoq 7 ocaml

OCaml允许从OCaml程序调用C函数,只要程序员遵循本手册"使用OCaml连接C"一章中的说明即可.

遵循这些指令时,本机编译器会将对C函数的调用转换为:

    movq    ml_as_z_sub@GOTPCREL(%rip), %rax
    call    caml_c_call@PLT
Run Code Online (Sandbox Code Playgroud)

(amd64指令集在这里,但在查看其他架构时,该方案似乎相当统一).

该函数caml_c_call最终执行计算跳转call *%rax,但它在之前和之后执行了很多操作.来自asmrun/amd64.S:

/* Call a C function from Caml */

FUNCTION(G(caml_c_call))
.Lcaml_c_call:
    /* Record lowest stack address and return address */
        popq    %r12
        STORE_VAR(%r12, caml_last_return_address)
        STORE_VAR(%rsp, caml_bottom_of_stack)
    /* Make the exception handler and alloc ptr available to the C code */
        STORE_VAR(%r15, caml_young_ptr)
        STORE_VAR(%r14, caml_exception_pointer)
    /* Call the function (address in %rax) */
        call    *%rax
    /* Reload alloc ptr */
        LOAD_VAR(caml_young_ptr, %r15)
    /* Return to caller */
        pushq   %r12
        ret
Run Code Online (Sandbox Code Playgroud)

当一个人想要经常执行一些既不分配也不提出异常的指令时,上面的内容有点矫枉过正.

有没有人有任何直接从OCaml调用小程序集的经验,而无需通过caml_c_call存根?这可能涉及欺骗本机编译器以为它正在调用ML函数或修改编译器.

问题出在Zarith库的上下文中,其中小的汇编代码可以直接计算和返回大多数结果,而不必经历caml_c_call,只跳转到caml_c_code需要分配或异常的困难参数.有关可以直接执行的汇编位的示例,请参阅此文件.

ygr*_*rek 8

也许"noalloc"和"float"可能有用吗?

PS一些更相关的链接.


Jef*_*eld 5

听起来好像您不介意OCaml函数调用的开销,如果您要调用的函数可以用汇编语言编写。我只是做了一些实验,您可以按照上面概述的方法进行操作。

这就是我所做的。为了获得可行的汇编语言模板,我在OCaml中定义了一个简单函数,并使用-S标志进行了编译。

$ cat sep.ml
let addto x = x + 1
$ /usr/local/ocaml312/bin/ocamlopt -inline 0 -c -S sep.ml
Run Code Online (Sandbox Code Playgroud)

注意:您需要指定-inline 0以确保ocamlopt从生成的.o文件中而不是从.cmx文件中的内联定义中获取代码。

现在,您有一个名为sep.s的文件。该addto函数如下所示(实际上是非常好的代码):

_camlSep__addto_1030:
.L100:
        addq    $2, %rax
        ret
Run Code Online (Sandbox Code Playgroud)

只是为了测试,我将2(在OCaml中代表1)更改为4(在OCaml中代表2)。因此,您现在拥有:

_camlSep__addto_1030:
.L100:
        addq    $4, %rax
        ret
Run Code Online (Sandbox Code Playgroud)

现在汇编该文件,生成一个不同版本的sep.o。

$ as -o sep.o sep.s
Run Code Online (Sandbox Code Playgroud)

本质上,您欺骗ocamlopt将sep.o中的代码当作是在OCaml中进行编码一样对待。但是您可以自己在汇编中编写代码(如果小心不要违反任何体系结构假设)。

您可以将其链接到主程序中并运行它:

$ cat main.ml
let main () =
    Printf.printf "%d\n" (Sep.addto 19)

let () = main ()
$ /usr/local/ocaml312/bin/ocamlopt -o main sep.cmx main.ml
$ main
21
Run Code Online (Sandbox Code Playgroud)

如您所见,它运行修改后的汇编代码。

您可以按照以下过程在汇编代码中创建任何OCaml可调用的函数。只要您不介意OCaml函数调用的开销,此方法就可以满足您的要求。

我不知道这种诡计将如何影响调试和垃圾回收的处理,因此我不会尝试使用可以进行任何分配的函数来进行尝试。

这些测试是在Mac OS X 10.6.8上使用OCaml 3.12.0(原始的64位版本)运行的。当我以“ as”运行时,我正在从Xcode 4.0.2运行库存的OS X汇编程序,该汇编程序默认情况下使用x86_64体系结构。

  • 这正是我最初的初衷,但是在ygrek的链接中,这表明您可以使用外部函数的“ noalloc”注释获得几乎相同的想法:http://camltastic.blogspot.com/2008/08 /tip-calling-c-functions-direct-with.html (2认同)