Hello Waldek, *,
Le ven. 1 mars 2024 à 01:33, Waldek Hebisch <
de...@fricas.org> a écrit :
>
> On Thu, Feb 29, 2024 at 01:01:45PM +0100, Grégory Vanuxem wrote:
> > Hello,
> >
> > I hesitated to add this as an issue at GitHub.com (feature request), I
> > put it here first.
> >
> > FriCAS supports what I would call preliminary support of FFI stuff via
> > a set of CL macros. I would like to suggest adding more types in this
> > regard. For example, it could be possible to add double-float arrays,
> > boolean and the like. I modified, and well tested, the following for
> > example. For information, only for SBCL and Clozure CL, but adding
> > those additional types for other CL implementations should be easy I
> > think:
>
>
> Well, there are some things which are easy, but a limited use, like
> adding 'void' or 'boolean' (but even boolean is not entirely
> strightforward, as one needd to know corresponding C type, which
> in principle could be 32-bit integer (I did not check what it is
> in various implementations, but AFAICS C compiler can choose size
> that it finds most convenient)).
Yes, I agree, it's not totally satisfactory, but it already allows you
to easily and succinctly code an interface to a C library, for
example. In general, booleans require special handling. With Clozure
CL, for example, a wrapper function is required, and this also applies
to strings. This is what I'm using for the moment:
(defmacro boot::|jl_bool_function_dbl_dbl| (func arg1 arg2)
`(if (eq (jl_bool_function_dbl_dbl ,func ,arg1 ,arg2) 0) nil t))))
(defmacro boot::|jl_string_eval_string| (str)
`(ccl::%get-cstring (jl_string_eval_string ,str))
It's very practical, by the way. Using this set of macros has been on
my TODO list for some time and I don't like modifying FriCAS
internals. What I'd like in interfacing with Julia is to be able to
produce a very simple patch to apply to vanilla FriCAS. And then be
able to add different spad files if/when necessary.
> IMO before going forward with implementation we should first
> define what FFI should do. One basic question is "who owns
> the data". sbcl FFI slightly prefers variant when C code
> owns data. This avoid memory management by Lisp implementation.
> But it also means that memory management is user responsibility.
> This may be acceptable when you want to use existing C library
> which requires manual memory management anyway. But for me
> _main_ use case is using C code as fast "primitives" that
> operate on Lisp data.
Very interesting. I know I do not take into account, right now,
stack/heap allocation differences between the different CL
implementations. I have to look deeper into this.
Presently I first need to manage the interaction
between two GCs, that is not really funny, but necessary.
I also prefer using "fast "primitives" that operate on Lisp data", I
use this scheme for parts of BLAS/LAPACK interface, but since I use a
high level interface this is not always possible to use "inplace"
operations. BTW there is also a partial BLAS/LAPACK interface in Julia that
uses the historical Fortran scheme, and you know that, with Fortran
you manage yourself memory areas (arrays) and just give them as
function/subroutine arguments.
> Concerning Lisp arrays, some years ago Clozure CL folks claimed
> that the only correct way to pass Lisp arrays is to create temporary
> copy in specially allocated memory and pass that to C code.
I did not know that, personally, with Clozure CL I use
'ccl::with-pointer-to-ivector' before calling C functions.
> Basically, Clozure CL can move normal Lisp objects at any time, so
> C code is not allowed direct access to Lisp data. In sbcl there is
> 'with-pinned-objects' which solves this problem.
Yes for SBCL. I was not aware of that for Clozure CL. To speak frankly
I am struggling with the Clozure CL GC, it
"never" recalls unreferenced memory, I have to write an email to
openmcl-devel to know how to _really_ do a GC reclaim. Maybe my code
is wrong, I do not "see" it reclaiming memory (with C printf help).
BTW it is always possible to temporarily stop the GC job but of course
this is not a solution that's too hard (and awful).
> ECL does not moves
> Lisp object (for ease of C interface) so no trouble here. IIUC GCL
> has special "non-movable" allocation for things that are passed via
> FFI, but with if we want freely pass Lisp arrays to FFI we would have
> to allocate all of them in non-movable way and it is not clear to me
> if this would fly if we allocate _all_ arrays in non-movable way. For
> Clisp it seems that we need to copy. Poplog has similar approach to
> GCL: there is non-movable pool of allocations for FFI.
I note. Supporting other CL implementations is on my road for what I'm doing.
>
> So, one issue is how we properly pass Lisp objects to C. Note:
> passing test do not really tell you if the code is correct. AFAIK
> Lisp moves object during garbage collection, so to get bad behaviour
> there are several conditions. Murphy says that such conditions will
> never happen during test but only when what you compute is important
> enough.
:)
I like his "law". Funnily expressed in french in fact, but when you
know what his job was you understand it: that will arrive...
>
> Another issue is how to specify foreign routines and foreign types
> in Spad. One possiblity is modified 'import' statement. Minimally
> one need here ablity to specify type. But frequently when using
> FFI foreign name is different that the name we would like to use
> in Spad, so probably we should add optional renaming. Instead
> of 'import' statement we could try to add "foreign packages",
> that is use normal package heading to specify types and add
> some magic token like "Foreign('C)" as implementation so that
> Spad compiler knows that is should create foreing calls for
> all functions.
Very ambitious, but that would be marvelous. I totally agree with Ralf
concerning $Lisp call vs. spad calls, I have a lot of things to clean
up in this regard. Calling routines using a specific FFI at the spad
level would be very clean. Using Lisp/C wrapper etc. is really not
satisfactory.
- Greg