Hi,
I've just pushed a collection of changes to master that have been in the
pipeline for quite a long time. Some of these are internal changes to
the run-time system and some are extensions, such as the addition of
IPv6 networking with INet6Sock and Net6HostDB structures.
The major change, though, is with the foreign function interface. On
X86 platforms libffi is no longer used and the version of it included in
the libpolyml directory has been removed. Libffi is still used in the
interpreted version but only if the library is installed on the system.
Instead the foreign function interface is handled essentially as part of
the compiler. The high-level interface in the Foreign structure remains
unchanged but the buildCallN functions now actually compile interface
functions. This results in foreign function calls being substantially
faster than with libffi; at least 10 times faster for trivial calls on
the X86/64. The cost is, of course, some extra work when buildCallN is
called, meaning that it is essential that these functions are only used
at the top level.
The reason for the speed-up is that the interface has to place the
arguments in the correct registers for the ABI and the rules for placing
arguments can be quite complicated, particular on the X86/64 on Unix.
Libffi computes the placement on every call whereas the compiler can do
this once and build code that moves the arguments into the right
registers and returns the result.
For backwards compatibility buildClosureN functions have been retained
but these are wrappers around new buildCallback functions. The
buildCallback functions differ in two respects. Closures created with
buildCallback are garbage-collected which means that if they are used to
register callbacks with a C library it may be necessary to keep a
reference in ML. There is a touchClosure function that should be called
when the callback is no longer needed. For compatibility closures
created with buildClosure are retained in a global list to avoid garbage
collection.
The other difference is that the buildCallback functions have a slightly
different type from buildClosure and that reflects the underlying
implementation. For example,
val buildCallback1: 'a conversion * 'b conversion ->
('a -> 'b) ->
('a -> 'b) closure
The first application builds the interface code that handle the
conversion between the ML and C ABIs. The second application applies
this to an ML function to build a closure value that can be passed to C.
This second application builds a small additional piece of code that
simply loads the address of the ML function into a register and jumps to
the interface code. What this means is that while the first application
should always be done at the top level it is possible to embed an
application of this to a particular ML function inside ML code. There
is still an overhead compared with creating a closure in ML and it is
better if possible to do the application at the top level but the cost
is significantly less than if the whole buildCallback function were
called within a function.
As always please give this a try and let me know if there are problems.
Regards,
David