OCaml允许从OCaml程序调用C函数,只要程序员遵循本手册“使用OCaml连接C”一章中的说明。
遵循这些说明时,本机编译器会将对C函数的调用转换为:
movq ml_as_z_sub@GOTPCREL(%rip), %rax
call caml_c_call@PLT
(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
当一个人想要经常执行一些既不分配也不引发异常的指令时,上面的内容有点矫枉过正。
有没有人有直接从OCaml调用小程序集例程的经验,而不通过caml_c_call
存根?这可能涉及欺骗本机编译器以为它正在调用ML函数或修改编译器。
问题出在Zarith库的上下文中,其中代码的小组件可以直接计算并返回大多数结果,而不必通过caml_c_call
,只跳转到caml_c_code
需要分配或例外的困难论据。有关可以直接执行的汇编位的示例,请参阅this file。
答案 0 :(得分:8)
也许"noalloc"和“浮动”可能有用吗?
PS更多related links。
答案 1 :(得分:5)
如果您调用的函数可以用汇编编写,听起来您不会介意OCaml函数调用的开销。我刚做了一些实验,你可以通过我上面概述的方法做到这一点。
这就是我的所作所为。为了获得可行的汇编语言模板,我在OCaml中定义了一个简单的函数,并使用-S标志进行编译。
$ cat sep.ml
let addto x = x + 1
$ /usr/local/ocaml312/bin/ocamlopt -inline 0 -c -S sep.ml
注意:您需要指定-inline 0
以确保ocamlopt从生成的.o文件中获取代码,而不是从.cmx文件中的内联定义中获取代码。
现在你有了一个名为sep.s.的文件。 addto
函数看起来像这样(非常好
代码,实际上):
_camlSep__addto_1030:
.L100:
addq $2, %rax
ret
仅仅为了测试,我将2(在OCaml中代表1)更改为4(在OCaml中代表2)。所以你现在有:
_camlSep__addto_1030:
.L100:
addq $4, %rax
ret
现在组装此文件,生成sep.o的异常版本。
$ as -o sep.o sep.s
从本质上讲,你已经欺骗了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
如您所见,它运行修改后的汇编代码。
您可以按照此过程创建任何OCaml可调用函数 汇编代码。只要你不介意OCaml函数调用的开销,这种方法可能会做你想要的。
我不知道这个技巧会如何影响调试和垃圾收集的处理,所以我不会尝试使用任何分配的函数。
这些测试使用OCaml 3.12.0(股票64位版本)在Mac OS X 10.6.8上运行。当我运行“as”时,我正在运行Xcode 4.0.2中的库存OS X汇编程序,它默认使用x86_64架构。
答案 2 :(得分:2)
在我看来,欺骗编译器认为它正在调用OCaml函数是没有用的,除非你也欺骗它进行内联调用。据我所知,通过读取来源,内联函数表示为Ulambda代码,后者又包含原语。因此,无论如何,这种思路导致为您的Zarith操作添加原语。如果你这样做,你有一个好的(并非一点都不棘手)解决方案,但它可能比你想做的更多。
对于一个非常棘手的方法,您可以尝试对生成的asm代码进行后处理以删除函数调用并使用内联代码替换它们。这种技巧已被多次使用。它通常不会持久,但短期内可能已经足够好了。 为此,您只需为OCaml编译器提供要运行的其他汇编程序的名称,即在汇编之前进行修改。