133 lines
5.1 KiB
Text
133 lines
5.1 KiB
Text
|
|
July 2005
|
||
|
|
These details were kindly provided by Duane Rettig of Franz.
|
||
|
|
|
||
|
|
Regarding the following snippet of the macro expansion of
|
||
|
|
FF:DEF-FOREIGN-CALL:
|
||
|
|
|
||
|
|
(SYSTEM::FF-FUNCALL
|
||
|
|
(LOAD-TIME-VALUE (EXCL::DETERMINE-FOREIGN-ADDRESS
|
||
|
|
'("foo" :LANGUAGE :C) 2 NIL))
|
||
|
|
'(:INT (INTEGER * *)) ARG1
|
||
|
|
'(:DOUBLE (DOUBLE-FLOAT * *)) ARG2
|
||
|
|
'(:INT (INTEGER * *)))
|
||
|
|
|
||
|
|
"
|
||
|
|
... in Allegro CL, if you define a foreign call FOO with C entry point
|
||
|
|
"foo" and with :call-direct t in the arguments, and if other things are
|
||
|
|
satisfied, then if a lisp function BAR is compiled which has a call to
|
||
|
|
FOO, that call will not go through ff-funcall (and thus a large amount
|
||
|
|
of argument manipulation and processing) but will instead set up its
|
||
|
|
arguments directly on the stack, and will then perform the "call" more
|
||
|
|
or less directly, through the "entry vec" (a small structure which
|
||
|
|
keeps track of a foreign entry's address and status)."
|
||
|
|
|
||
|
|
This is the code that generates what the compiler expects to see:
|
||
|
|
|
||
|
|
(setq call-direct-form
|
||
|
|
(if* call-direct
|
||
|
|
then `(setf (get ',lispname 'sys::direct-ff-call)
|
||
|
|
(list ',external-name
|
||
|
|
,callback
|
||
|
|
,convention
|
||
|
|
',returning
|
||
|
|
',arg-types
|
||
|
|
,arg-checking
|
||
|
|
,entry-vec-flags))
|
||
|
|
else `(remprop ',lispname 'sys::direct-ff-call)))
|
||
|
|
|
||
|
|
Thus generating something like:
|
||
|
|
|
||
|
|
(EVAL-WHEN (COMPILE LOAD EVAL)
|
||
|
|
(SETF (GET 'FOO 'SYSTEM::DIRECT-FF-CALL)
|
||
|
|
(LIST '("foo" :LANGUAGE :C) T :C
|
||
|
|
'(:INT (INTEGER * *))
|
||
|
|
'((:INT (INTEGER * *))
|
||
|
|
(:FLOAT (SINGLE-FLOAT * *)))
|
||
|
|
T
|
||
|
|
2 ; this magic value is explained later
|
||
|
|
)))
|
||
|
|
|
||
|
|
"
|
||
|
|
(defun determine-foreign-address (name &optional (flags 0) method-index)
|
||
|
|
;; return an entry-vec struct suitable for the foreign-call of name.
|
||
|
|
;;
|
||
|
|
;; name is either a string, which is taken without conversion, or
|
||
|
|
;; a list consisting of a string to convert or a conversion function
|
||
|
|
;; call.
|
||
|
|
;; flags is an integer representing the flags to place into the entry-vec.
|
||
|
|
;; method-index, if non-nil, is a word-index into a vtbl (virtual table).
|
||
|
|
;; If method-index is true, then the name must be a string uniquely
|
||
|
|
;; represented by the index and by the flags field.
|
||
|
|
|
||
|
|
Note that not all architectures implement the :method-index argument
|
||
|
|
to def-foreign-call, but your interface likely won't support it
|
||
|
|
anyway, so just leave it nil. As for the flags, they are constants
|
||
|
|
stored into the entry-vec returned by d-f-a and are given here:
|
||
|
|
|
||
|
|
(defconstant ep-flag-call-semidirect 1) ; Real address stored in alt-address slot
|
||
|
|
(defconstant ep-flag-never-release 2) ; Never release the heap
|
||
|
|
(defconstant ep-flag-always-release 4) ; Always release the heap
|
||
|
|
(defconstant ep-flag-release-when-ok 8) ; Release the heap unless without-interrupts
|
||
|
|
|
||
|
|
(defconstant ep-flag-tramp-calls #x70) ; Make calls through special trampolines
|
||
|
|
(defconstant ep-flag-tramp-shift 4)
|
||
|
|
|
||
|
|
(defconstant ep-flag-variable-address #x100) ; Entry-point contains address of C var
|
||
|
|
(defconstant ep-flag-strings-convert #x200) ; Convert strings automatically
|
||
|
|
|
||
|
|
(defconstant ep-flag-get-errno #x1000) ;; [rfe5060]: Get errno value after call
|
||
|
|
(defconstant ep-flag-get-last-error #x2000) ;; [rfe5060]: call GetLastError after call
|
||
|
|
;; Leave #x4000 and #x8000 open for expansion
|
||
|
|
|
||
|
|
Mostly, you'll give the value 2 (never release the heap), but if you
|
||
|
|
give 4 or 8, then d-f-a will automatically set the 1 bit as well,
|
||
|
|
which takes the call through a heap-release/reacquire process.
|
||
|
|
|
||
|
|
Some docs for entry-vec are:
|
||
|
|
|
||
|
|
;; -- entry vec --
|
||
|
|
;; An entry-vec is an entry-point descriptor, usually a pointer into
|
||
|
|
;; a shared-library. It is represented as a 5-element struct of type
|
||
|
|
;; foreign-vector. The reason for this represntation is
|
||
|
|
;; that it allows the entry point to be stored in a table, called
|
||
|
|
;; the .saved-entry-points. table, and to be used by a foreign
|
||
|
|
;; function. When the location of the foreign function to which the entry
|
||
|
|
;; point refers changes, it is simply a matter of changing the value in entry
|
||
|
|
;; point vector and the foreign call code sees it immediately. There is
|
||
|
|
;; even an address that can be put in the entry point vector that denotes
|
||
|
|
;; a missing foreign function, thus lookup can happen dynamically.
|
||
|
|
|
||
|
|
(defstruct (entry-vec
|
||
|
|
(:type (vector excl::foreign (*)))
|
||
|
|
(:constructor make-entry-vec-boa ()))
|
||
|
|
name ; entry point name
|
||
|
|
(address 0) ; jump address for foreign code
|
||
|
|
(handle 0) ; shared-lib handle
|
||
|
|
(flags 0) ; ep-* flags
|
||
|
|
(alt-address 0) ; sometimes holds the real func addr
|
||
|
|
)
|
||
|
|
|
||
|
|
[...]
|
||
|
|
"
|
||
|
|
|
||
|
|
Regarding the arguments to SYSTEM::FF-FUNCALL:
|
||
|
|
'(:int (integer * *)) argN
|
||
|
|
|
||
|
|
"The type-spec is as it is given in the def-foreign-call
|
||
|
|
syntax, with a C type optionally followed by a lisp type,
|
||
|
|
followed optionally by a user-conversion function name[...]"
|
||
|
|
|
||
|
|
|
||
|
|
Getting the alignment:
|
||
|
|
|
||
|
|
CL-USER(2): (ff:get-foreign-type :int)
|
||
|
|
#S(FOREIGN-FUNCTIONS::IFOREIGN-TYPE
|
||
|
|
:ATTRIBUTES NIL
|
||
|
|
:SFTYPE
|
||
|
|
#S(FOREIGN-FUNCTIONS::SIZED-FTYPE-PRIM
|
||
|
|
:KIND :INT
|
||
|
|
:WIDTH 4
|
||
|
|
:OFFSET 0
|
||
|
|
:ALIGN 4)
|
||
|
|
...)
|