In the Haskell2010 report, from section 8.4.2, there is a list of basic types that are supported in FFI import/export declarations:
The following types constitute the set of basic foreign types:
Char
,Int
,Double
,Float
, andBool
as exported by the Haskell Prelude as well asInt8
,Int16
,Int32
,Int64
,Word8
,Word16
,Word32
,Word64
,Ptr a
,FunPtr a
, andStablePtr a
, for any typea
, as exported by Foreign (Section 24).A Haskell system that implements the FFI needs to be able to pass these types between the Haskell and the external context as function arguments and results.
From the GHC manual section on extensions on FFI, GHC adds to these types with some unboxed types:
The following unboxed types may be used as basic foreign types (see FFI Addendum, Section 3.2):
Int#
,Word#
,Char#
,Float#
,Double#
,Addr#
,StablePtr# a
,MutableByteArray#
,ForeignObj#
, andByteArray#
.
It is pretty clear what happens for most of theses types at the level of the linker when i specify the ccall
calling convention. For example, I'm pretty sure Int
/Int#
get passed on the stack as two 32-bit values. Similarly, Ptr a
/StablePtr a
/StablePtr# a
/Addr#
probably all get passed as pointers on the stack.
What about ByteArray#
and MutableByteArray#
?
All I can imagine is passing them as pointers, but that seems a bit nuts since, unless you make your ByteArray#
/MutableByteArray#
pinned, the GHC runtime might end up moving the array from under you. Also, you would be ignoring the size information also on the array.
Thanks @ThomasM.DuBuisson for digging up an old email thread:
There is a way to pass an unpinned
ByteArray#
(orMutableByteArray#
, but the former seems right in your case) to a foreign call, using theUnliftedFFITypes
language extension. TheByteArray#
is guaranteed to not to be moved for the duration of the call. The code should treat theByteArray#
argument as if it was a pointer to bytes. You will need to do any address offset computations on the C side (i.e. pass any offsets you need as extra argument to your C function).
So: these are passed as pointers and GHC's runtime promises not to move them from under you.