OCaml allows C functions to be called from OCaml programs, as long as the programmer follows the instructions in the "Interfacing C with OCaml" chapter of the manual.
When following these instructions, a call to a C function is translated by the native compiler to:
movq ml_as_z_sub@GOTPCREL(%rip), %rax
call caml_c_call@PLT
(amd64 instruction set here, but looking at other architectures, the scheme seems to be rather uniform).
The function caml_c_call
eventually does a computed jump call *%rax
, but it does a lot of things before and after. From 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
When one wants to frequently execute a couple of instructions that neither allocate nor raise exceptions, the above is a little bit overkill.
Does anyone have any experience in calling a small assembly routine directly from OCaml, without going through the caml_c_call
stub? This probably involves tricking the native compiler into thinking that it is calling an ML function, or modifying the compiler.
The question is in the context of the library Zarith, where small assembly bits of code could compute and return most results directly, without having to go through caml_c_call
, and only jump to caml_c_code
for the difficult arguments that require allocation or exceptions. See this file for examples of assembly bits that could be executed directly.
Maybe "noalloc" and "float" could be of some use?
PS some more related links.
It seems to me it doesn't help to trick the compiler into thinking it's calling an OCaml function, unless you also trick it into inlining the call. As far as I can tell by perusing sources, inlined functions are expressed in something called Ulambda code, which in turn contains primitives. So this line of thinking, anyway, leads to adding primitives for your Zarith operations. If you do that, you have a good (not at all tricky) solution, but it might be more work than you want to do.
For a really tricky approach, you could try post-processing the generated asm code to remove function calls and replace them with in-line code. This kind of trick has been used many times. It usually doesn't hold up for long, but it might be good enough for the short term. To do this, you'd just give the OCaml compiler the name of a different assembler to run, one that does your modifications before assembling.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With