[MLton] Support for link options in ML Basis files
Matthew Fluet
fluet@cs.cornell.edu
Tue, 18 Jan 2005 13:39:35 -0500 (EST)
> > > Can we avoid portability problems by automatically generating a
> > > wrapper to translate between the calling convention we understand
> > > (n-ary C functions with basic types) and the calling convention we
> > > don't (passing C structs)?
> >
> > That is an option.
> ...
> > The problem is that on the ML side, ML-NLFFI always passes structs/unions
> > around as pointers.
>
> The approach I proposed seems easier, although both work. I think the
> difference is in who allocates space for the struct. In my approach,
> the struct is allocated on the stack on the C side and then
> immediately flattened to pass to the funtion. In their approach, if I
> understand correctly, the struct is allocated on the SML side (using
> malloc? or SML/NJ alloc?) and then flattened on the C side to pass to
> the function.
I think you understand correctly, but you are also missing the bigger
scenario. Suppose I have a c file like:
structs.c:
#include <stdlib.h>
#include <stdio.h>
struct foo {
int i;
double j;
};
typedef struct foo foo_str;
void print_foo_str (foo_str foo) {
printf (" C::print_foo_str: foo.i = %d foo.j = %f\n",
foo.i, foo.j);
}
foo_str make_foo_str (int i, double j) {
foo_str f;
printf (" C::make_foo_str: i = %d j = %f\n", i, j);
f.i = i;
f.j = j;
return f;
}
Then, I will get, among other things, the following files/structures:
f-print_foo_str.sml:
structure F_print_foo_str : sig
val typ :
(((ST_foo.tag, ro) su_obj' -> unit) fptr,
(ST_foo.tag, ro) su_obj' -> unit) T.typ
val fptr : unit -> ((ST_foo.tag, ro) su_obj' -> unit) fptr
val f : (ST_foo.tag, 'c) su_obj -> unit
val f' : (ST_foo.tag, 'c) su_obj' -> unit
end = struct
...
end
f-make_foo_str.sml:
structure F_make_foo_str : sig
val typ :
(((ST_foo.tag, rw) su_obj' * sint * double ->
(ST_foo.tag, rw) su_obj') fptr,
(ST_foo.tag, rw) su_obj' * sint * double ->
(ST_foo.tag, rw) su_obj') T.typ
val fptr :
unit ->
((ST_foo.tag, rw) su_obj' * sint * double ->
(ST_foo.tag, rw) su_obj') fptr
val f :
(ST_foo.tag, rw) su_obj * MLRep.Int.Signed.int * MLRep.Double.real ->
(ST_foo.tag, rw) su_obj
val f' :
(ST_foo.tag, rw) su_obj' *
MLRep.Int.Signed.int *
MLRep.Double.real -> (ST_foo.tag, rw) su_obj'
end = struct
...
end
Note that the function I get to call, which had C-prototype:
void print_foo_str (foo_str foo);
is available with ML-type:
val f : (ST_foo.tag, 'c) su_obj -> unit
not with ML-type:
val f : {i : Int32.int, d : Real64.real} -> unit
So, to call the ML function, you have to have a (ST_foo.tag, 'c) su_obj,
which you might get from the translation of make_foo_str.
The ML-NLFFI Library represents (ST_foo.tag, 'c) su_obj as the address of
the allocated structure (the two type arguments are phantom).
The point being that when I call print_foo_str, the structure is already
allocated (in a malloc-ed area of memory) and I only have the pointer to
the structure.
> Hopefully my approach will be faster because gcc can
> optimize away the struct entirely, and just shuffle the args to
> translate between the n-ary calling convention and the struct calling
> convention.
I don't know if it will be necessarily faster, because you will first need
to extract each field from the (pointer to the) struct, which are then
shuffled to translate between the n-ary calling convention and the struct
calling convention.
> I guess the problem is that you will have to modify ML-NLFFI to do
> this.
To accomplish what you have in mind, on the SML side, you want to
represent structures as SML records. I'm not sure that you can preserve
the ML-NLFFI interface to support that representation.