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需要分配或异常的困难参数.有关可以直接执行的汇编位的示例,请参阅此文件.
听起来好像您不介意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体系结构。