C / Assembly function call from OCaml more directly than with caml_c_call

OCaml allows you to call C functions from OCaml programs if the programmer follows the instructions in the "C Interface with OCaml" chapter of the manual.

When these instructions are executed, the function call C is translated using the built-in compiler to:

movq ml_as_z_sub@GOTPCREL (%rip), %rax call caml_c_call@PLT 

(the amd64 instruction is here, but looking at other architectures, the circuit seems pretty uniform).

The caml_c_call function ultimately performs the calculated call *%rax , but it does a lot 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 

If you want to frequently execute a couple of instructions that do not throw out and do not throw exceptions, the above is a bit overpriced.

Does anyone have experience calling a small build procedure directly from OCaml without going through the caml_c_call stub? This is probably due to the deception of the native compiler, believing that it calls the ML function or modifies the compiler.

The question is in the context of the Zarith library, where small bits of code assembly can calculate and return most of the results directly, without having to go through caml_c_call and go only to caml_c_code for complex arguments that require allocation or exclusion. See this file for examples of assembly bits that can be executed directly.

+6
source share
3 answers

Maybe "noalloc" and "float" might come in handy?

PS some more related links .

+8
source

It looks like you don't mind the overhead of calling an OCaml function if the function you called can be written in the assembly. I just did some experiments, and you can do it using the method described above.

Here is what I did. To get a working assembly language template, I defined a simple function in OCaml and compiled with the -S flag.

 $ cat sep.ml let addto x = x + 1 $ /usr/local/ocaml312/bin/ocamlopt -inline 0 -c -S sep.ml 

Note: you need to specify -inline 0 to ensure that ocamlopt takes the code from your generated .o file, and not from the inline definition in the .cmx file.

You now have a file called sep.s. The addto function looks like this (surprisingly good code, actually):

 _camlSep__addto_1030: .L100: addq $2, %rax ret 

For the test only, I changed 2 (which represents 1 in OCaml) to 4 (which represents 2 in OCaml). So now you have:

 _camlSep__addto_1030: .L100: addq $4, %rax ret 

Now compile this file by creating a deviant version of sep.o.

 $ as -o sep.o sep.s 

Essentially, you tricked ocamlopt into processing the code in sep.o as if it were encoded in OCaml. But you can write the code yourself in the assembly (if you do not violate any architectural assumptions).

You can link it to the main program and run it:

 $ 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 

As you can see, it runs the modified assembly code.

You can follow this procedure to create any OCaml functions called into the assembly code. If you don't mind the overhead of calling an OCaml function, this approach can do what you want.

I do not know how this trick will affect debugging and garbage collection processing, so I would not try this with a function that makes any allocations.

These tests were run on Mac OS X 10.6.8 using OCaml 3.12.0 (64-bit build). When I run the how, I run the OS X builder with Xcode 4.0.2, which uses the x86_64 architecture by default.

+4
source

It seems to me that this does not help to trick the compiler into thinking that it calls the OCaml function, unless you also trick it to insert a call. As far as I can tell by looking at the sources, inline functions are expressed in code called Ulambda, which in turn contains primitives. In any case, this line of thinking leads to the addition of primitives for your operations with Zarit. If you do, you have a good (not at all complicated) solution, but it may be more work than you want.

For a really complicated approach, you can try to process the generated asm code to remove function calls and replace them with inline code. This trick has been used many times. Usually it does not linger for a long time, but may be good enough for the short-term. To do this, you just need to tell the OCaml compiler the name of another assembler that will perform your changes before building.

+2
source

Source: https://habr.com/ru/post/898080/


All Articles