Gregory Nisbet Gregory Nisbet - 1 month ago 19
C Question

OCaml return unboxed float from C function with bytecode compiler

I have a very simple function that calls

time(2)
in order to get the epoch time and returns it as an unboxed OCaml float.

The platform here is OS X 10.12.6 and I've tested using OCaml versions 4.03.0 and 4.04.0 .

Here is
libunixtime.c


#define CAML_NAME_SPACE
#include <time.h>
#include <caml/memory.h>

#define IGNORE_UNUSED(x) ( (void)(x) )

CAMLprim double
ocaml_unixtime(value unit)
{
IGNORE_UNUSED(unit);
return (double) time(NULL);
}


The OCaml code,
unixtime.ml
looks like this ... it returns an unboxed float and indicates to the compiler that it doesn't perform any OCaml-side allocations.

This function takes less than 5 arguments, so it seems like implementing it once and using that for both the byte code compiler and native compiler should be fine (https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html#sec397).

external unixtime : unit -> (float [@unboxed]) =
"ocaml_unixtime" "ocaml_unixtime" [@@noalloc];;

let () = Printf.printf "%f\n" (unixtime ())


This example works fine with the native compiler (although I'm a little concerned that the cast to
double
is not exactly the right thing to do but It Works On My Machine).

% ocamlopt unixtime.ml libunixtime.c
% ./a.out
1506195346.000000


However, when compiling with the bytecode compiler and attempting to run, I get a segfault immediately.

% ocamlc -custom unixtime.ml libunixtime.c
% ./a.out
Segmentation fault
Exit 139


Does the bytecode compiler not support unboxed floats? How do I figure out why the behavior differs between the bytecode and native compilers?

Answer Source

First, your code seems to be missing a CAMLparam call for the unit value. This is necessary so that the GC knows about it, see section 19.1.

But there's another more important bug: the bytecode version cannot use unboxed functions (section 19.10), so you'll have to provide an alternative implementation for the bytecode compiler that returns a boxed value using caml_copy_double and CAMLreturn.