Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a C/assembly function from OCaml more directly than with caml_c_call

Tags:

ocaml

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.

like image 731
Pascal Cuoq Avatar asked Sep 26 '11 14:09

Pascal Cuoq


2 Answers

Maybe "noalloc" and "float" could be of some use?

PS some more related links.

like image 100
ygrek Avatar answered Nov 07 '22 03:11

ygrek


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.

like image 26
Jeffrey Scofield Avatar answered Nov 07 '22 01:11

Jeffrey Scofield