我正在尝试用 OCaml 编写一个(非常小、简单——我不知道我在做什么,让我们成为现实吧!)编译器。
我真的很想避免将 任何 ISO-C 代码检查到项目中(尽管相当了解 C;这里的目标是专门学习和使用 OCaml。)据此,我需要在 OCaml 中为编译语言编写“运行时”,将其与主要项目分开编译,然后将其链接到编译器本身的输出。
不幸的是,它看起来像任何外部函数——即使是那些不接触任何 OCaml 数据结构/OCaml 堆的函数,都应该使用 OCaml 的 C 宏来构建:
CAMLprim value scheme_entry(value unit) {
int i;
i = 42;
return Val_int(i);
}
如果我自己发出汇编指令,这可能不是一个选项。 (至少,直到我学到更多!)
是否有任何方法(包括 hacky 方法——这是一个个人学习项目)从 OCaml 中调用像下面这样的极其简单的函数?
_scheme_entry:
movl $42 %eax
ret
For reference, I'm working through Ghuloum's IACC: http://ell.io/tt$ocameel
最佳答案
Unfortunately, it looks like any external functions — even ones that don't touch any OCaml data-structures / the OCaml heap, are expected to be constructed using OCaml's C macros:
不,他们不是。如果您的函数根本不接触 OCaml,或者不分配或使用分配的值,那么您可以直接调用函数,例如,
value scheme_entry(value unit) {
int i;
i = 42;
return Val_int(i);
}
在 OCaml 方面:
external scheme_entry : unit -> int = "scheme_entry" [@@noalloc]
在 OCaml 的最新版本中,我们获得了更多的控制权,因此我们可以在不装箱/拆箱的情况下传递 float 和更大的整数。阅读here了解更多信息。
请注意,Val_int
只是一个将 C 整数向左移动一位并将最低有效位设置为 1 的宏,即 (((unsigned)(x) << 1)) + 1)
.所以,如果你不需要将 OCaml 整数转换为 C 整数,反之亦然,那么你甚至不需要使用这个宏,所以你的运行时可以完全不知道 OCaml,例如:
void runtime_init(void) {
printf("Hello from runtime");
}
在 OCaml 方面:
val runtime_init : unit -> unit = "runtime_init" [@@noalloc]
注:[@@noalloc]
OCaml 4.03 中添加了属性,在该版本之前,您应该使用 "noalloc"
,例如,
val runtime_init : unit -> unit = "runtime_init" "noalloc"
当然,您的极其简单的函数应该遵守适用于您的特定体系结构和操作系统的 C 调用约定。因此,如果您的函数破坏了一些应该保留的寄存器,您应该预料到会有麻烦,例如在 amd64 ABI 上您应该保留 rbp、rbx、r12-r15。这个要求没有 OCaml 特定的内容,只是一个普通的 C 调用约定。
关于c - 是否可以在没有包装类型的情况下从 OCaml 调用 C 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46566761/