on master help sign in
Improve C FFI

Implement peek and poke for pointers Move peek, poke for bitstrings to idris_bitrsing Add access to global variables Add call of foreign functions via pointer This adds some new features to the C FFI that match what's in the Haskell FFI. Prefixing the name with `&` gives a pointer to a C global. ``` errno : IO Ptr errno = foreign FFI_C "&__errno" (IO Ptr) ``` Putting `%wrapper` in the name argument gives a function pointer to a wrapped Idris function (useful for APIs that take a struct with a bunch of functions). It's functionality matches "wrapper" in Haskell, but the illegal C name makes name clashes go away. Putting `%dynamic` in the name argument calls a C function from a function pointer. This is useful for APIs that provide structures with C functions (popular in object oriented C) or otherwise returns C function pointers. es in a declaration. The type signature in dynamic should take an extra Ptr up front for the function pointer.

commit91d583ee9210d7198c885f7fee0ff9afcecc1a0e authorNiklas Larsson <niklaslarsson@tedkomp.local> dateMon 8 Feb 2016, 14:32 (11 years ago) parent8d3cf2d
12 files changed+132−50
CHANGELOG+8 libs/prelude/Builtins.idr+4 libs/prelude/IO.idr+11−4 rts/idris_bitstring.c+35 rts/idris_bitstring.h+10 rts/idris_rts.c+6−30 rts/idris_rts.h+2−10 src/IRTS/CodegenC.hs+22−5 test/ffi007/expected+3 test/ffi007/ffi007.c+9 test/ffi007/ffi007.h+7 test/ffi007/ffi007.idr+15−1
CHANGELOG+8
@@ -15,6 +15,14 @@ Updated export rules
1515* Exported types can only refer to other exported names
1616* Publicly exported definitions can only refer to publicly exported names
1717
18+Improved C FFI
19+--------------
20+
21+* Idris functions can now be passed as callbacks to C functions or wrapped
22+in a C function pointer.
23+* C function pointers can be called.
24+* Idris can access pointers to C globals.
25+
1826Minor language changes
1927----------------------
2028
libs/prelude/Builtins.idr+4
@@ -192,6 +192,7 @@ export data ManagedPtr : Type
192192
193193-- primitives for accessing memory.
194194%extern prim__asPtr : ManagedPtr -> Ptr
195+%extern prim__sizeofPtr : Int
195196%extern prim__peek8 : prim__WorldType -> Ptr -> Int -> Bits8
196197%extern prim__peek16 : prim__WorldType -> Ptr -> Int -> Bits16
197198%extern prim__peek32 : prim__WorldType -> Ptr -> Int -> Bits32
@@ -201,3 +202,6 @@ export data ManagedPtr : Type
201202%extern prim__poke16 : prim__WorldType -> Ptr -> Int -> Bits16 -> Int
202203%extern prim__poke32 : prim__WorldType -> Ptr -> Int -> Bits32 -> Int
203204%extern prim__poke64 : prim__WorldType -> Ptr -> Int -> Bits64 -> Int
205+
206+%extern prim__peekPtr : prim__WorldType -> Ptr -> Int -> Ptr
207+%extern prim__pokePtr : prim__WorldType -> Ptr -> Int -> Ptr -> Int
libs/prelude/IO.idr+11−4
@@ -319,25 +319,32 @@ prim_peek8 ptr offset = MkIO (\w => prim_io_return (prim__peek8 (world w) ptr of
319319
320320prim_poke8 : Ptr -> Int -> Bits8 -> IO Int
321321prim_poke8 ptr offset val = MkIO (\w => prim_io_return (
322− prim__poke8 (world w) ptr offset val))
322+ prim__poke8 (world w) ptr offset val))
323323
324324prim_peek16 : Ptr -> Int -> IO Bits16
325325prim_peek16 ptr offset = MkIO (\w => prim_io_return (prim__peek16 (world w) ptr offset))
326326
327327prim_poke16 : Ptr -> Int -> Bits16 -> IO Int
328328prim_poke16 ptr offset val = MkIO (\w => prim_io_return (
329− prim__poke16 (world w) ptr offset val))
329+ prim__poke16 (world w) ptr offset val))
330330
331331prim_peek32 : Ptr -> Int -> IO Bits32
332332prim_peek32 ptr offset = MkIO (\w => prim_io_return (prim__peek32 (world w) ptr offset))
333333
334334prim_poke32 : Ptr -> Int -> Bits32 -> IO Int
335335prim_poke32 ptr offset val = MkIO (\w => prim_io_return (
336− prim__poke32 (world w) ptr offset val))
336+ prim__poke32 (world w) ptr offset val))
337337
338338prim_peek64 : Ptr -> Int -> IO Bits64
339339prim_peek64 ptr offset = MkIO (\w => prim_io_return (prim__peek64 (world w) ptr offset))
340340
341341prim_poke64 : Ptr -> Int -> Bits64 -> IO Int
342342prim_poke64 ptr offset val = MkIO (\w => prim_io_return (
343− prim__poke64 (world w) ptr offset val))
343+ prim__poke64 (world w) ptr offset val))
344+
345+prim_peekPtr : Ptr -> Int -> IO Ptr
346+prim_peekPtr ptr offset = MkIO (\w => prim_io_return (prim__peekPtr (world w) ptr offset))
347+
348+prim_pokePtr : Ptr -> Int -> Ptr -> IO Int
349+prim_pokePtr ptr offset val = MkIO (\w => prim_io_return (
350+ prim__pokePtr (world w) ptr offset val))
rts/idris_bitstring.c+35
@@ -822,3 +822,38 @@ VAL idris_b64T32(VM *vm, VAL a) {
822822 return cl;
823823}
824824
825+VAL idris_peekB8(VM* vm, VAL ptr, VAL offset) {
826+ return MKB8(vm, *(uint8_t*)(GETPTR(ptr) + GETINT(offset)));
827+}
828+
829+VAL idris_pokeB8(VAL ptr, VAL offset, VAL data) {
830+ *(uint8_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS8(data);
831+ return MKINT(0);
832+}
833+
834+VAL idris_peekB16(VM* vm, VAL ptr, VAL offset) {
835+ return MKB16(vm, *(uint16_t*)(GETPTR(ptr) + GETINT(offset)));
836+}
837+
838+VAL idris_pokeB16(VAL ptr, VAL offset, VAL data) {
839+ *(uint16_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS16(data);
840+ return MKINT(0);
841+}
842+
843+VAL idris_peekB32(VM* vm, VAL ptr, VAL offset) {
844+ return MKB32(vm, *(uint32_t*)(GETPTR(ptr) + GETINT(offset)));
845+}
846+
847+VAL idris_pokeB32(VAL ptr, VAL offset, VAL data) {
848+ *(uint32_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS32(data);
849+ return MKINT(0);
850+}
851+
852+VAL idris_peekB64(VM* vm, VAL ptr, VAL offset) {
853+ return MKB64(vm, *(uint64_t*)(GETPTR(ptr) + GETINT(offset)));
854+}
855+
856+VAL idris_pokeB64(VAL ptr, VAL offset, VAL data) {
857+ *(uint64_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS64(data);
858+ return MKINT(0);
859+}
rts/idris_bitstring.h+10
@@ -126,4 +126,14 @@ VAL idris_b64T8(VM *vm, VAL a);
126126VAL idris_b64T16(VM *vm, VAL a);
127127VAL idris_b64T32(VM *vm, VAL a);
128128
129+// memory access
130+VAL idris_peekB8(VM* vm, VAL ptr, VAL offset);
131+VAL idris_pokeB8(VAL ptr, VAL offset, VAL data);
132+VAL idris_peekB16(VM* vm, VAL ptr, VAL offset);
133+VAL idris_pokeB16(VAL ptr, VAL offset, VAL data);
134+VAL idris_peekB32(VM* vm, VAL ptr, VAL offset);
135+VAL idris_pokeB32(VAL ptr, VAL offset, VAL data);
136+VAL idris_peekB64(VM* vm, VAL ptr, VAL offset);
137+VAL idris_pokeB64(VAL ptr, VAL offset, VAL data);
138+
129139#endif
rts/idris_rts.c+6−30
@@ -422,39 +422,15 @@ void idris_poke(void* ptr, i_int offset, uint8_t data) {
422422 *(((uint8_t*)ptr) + offset) = data;
423423}
424424
425−VAL idris_peekB8(VM* vm, VAL ptr, VAL offset) {
426− return MKB8(vm, *(uint8_t*)(GETPTR(ptr) + GETINT(offset)));
427−}
428−
429−VAL idris_pokeB8(VAL ptr, VAL offset, VAL data) {
430− *(uint8_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS8(data);
431− return MKINT(0);
432−}
433−
434−VAL idris_peekB16(VM* vm, VAL ptr, VAL offset) {
435− return MKB16(vm, *(uint16_t*)(GETPTR(ptr) + GETINT(offset)));
436−}
437−
438−VAL idris_pokeB16(VAL ptr, VAL offset, VAL data) {
439− *(uint16_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS16(data);
440− return MKINT(0);
441−}
442−
443−VAL idris_peekB32(VM* vm, VAL ptr, VAL offset) {
444− return MKB32(vm, *(uint32_t*)(GETPTR(ptr) + GETINT(offset)));
445−}
446−
447−VAL idris_pokeB32(VAL ptr, VAL offset, VAL data) {
448− *(uint32_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS32(data);
449− return MKINT(0);
450−}
451425
452−VAL idris_peekB64(VM* vm, VAL ptr, VAL offset) {
453− return MKB64(vm, *(uint64_t*)(GETPTR(ptr) + GETINT(offset)));
426+VAL idris_peekPtr(VM* vm, VAL ptr, VAL offset) {
427+ void** addr = GETPTR(ptr) + GETINT(offset);
428+ return MKPTR(vm, *addr);
454429}
455430
456−VAL idris_pokeB64(VAL ptr, VAL offset, VAL data) {
457− *(uint64_t*)(GETPTR(ptr) + GETINT(offset)) = GETBITS64(data);
431+VAL idris_pokePtr(VAL ptr, VAL offset, VAL data) {
432+ void** addr = GETPTR(ptr) + GETINT(offset);
433+ *addr = GETPTR(data);
458434 return MKINT(0);
459435}
460436
rts/idris_rts.h+2−10
@@ -311,16 +311,8 @@ void idris_memmove(void* dest, void* src, i_int dest_offset, i_int src_offset, i
311311uint8_t idris_peek(void* ptr, i_int offset);
312312void idris_poke(void* ptr, i_int offset, uint8_t data);
313313
314−
315−// memory access
316−VAL idris_peekB8(VM* vm, VAL ptr, VAL offset);
317−VAL idris_pokeB8(VAL ptr, VAL offset, VAL data);
318−VAL idris_peekB16(VM* vm, VAL ptr, VAL offset);
319−VAL idris_pokeB16(VAL ptr, VAL offset, VAL data);
320−VAL idris_peekB32(VM* vm, VAL ptr, VAL offset);
321−VAL idris_pokeB32(VAL ptr, VAL offset, VAL data);
322−VAL idris_peekB64(VM* vm, VAL ptr, VAL offset);
323−VAL idris_pokeB64(VAL ptr, VAL offset, VAL data);
314+VAL idris_peekPtr(VM* vm, VAL ptr, VAL offset);
315+VAL idris_pokePtr(VAL ptr, VAL offset, VAL data);
324316
325317// String primitives
326318VAL idris_concat(VM* vm, VAL l, VAL r);
src/IRTS/CodegenC.hs+22−5
@@ -318,19 +318,26 @@ bcc i (TOPBASE n) = indent i ++ "TOPBASE(" ++ show n ++ ");\n"
318318bcc i (BASETOP n) = indent i ++ "BASETOP(" ++ show n ++ ");\n"
319319bcc i STOREOLD = indent i ++ "STOREOLD;\n"
320320bcc i (OP l fn args) = indent i ++ doOp (creg l ++ " = ") fn args ++ ";\n"
321−bcc i (FOREIGNCALL l rty (FStr fn) (x:xs)) | fn == "_idris_get_wrapper"
321+bcc i (FOREIGNCALL l rty (FStr fn@('&':name)) [])
322+ = indent i ++
323+ c_irts (toFType rty) (creg l ++ " = ") fn ++ ";\n"
324+bcc i (FOREIGNCALL l rty (FStr fn) (x:xs)) | fn == "%wrapper"
322325 = indent i ++
323326 c_irts (toFType rty) (creg l ++ " = ")
324− (fn ++ "(" ++ creg (snd x) ++ ")") ++ ";\n"
327+ ("_idris_get_wrapper(" ++ creg (snd x) ++ ")") ++ ";\n"
328+bcc i (FOREIGNCALL l rty (FStr fn) (x:xs)) | fn == "%dynamic"
329+ = indent i ++ c_irts (toFType rty) (creg l ++ " = ")
330+ ("(*(" ++ cFnSig "" rty xs ++ ") GETPTR(" ++ creg (snd x) ++ "))" ++
331+ "(" ++ showSep "," (map fcall xs) ++ ")") ++ ";\n"
325332bcc i (FOREIGNCALL l rty (FStr fn) args)
326333 = indent i ++
327334 c_irts (toFType rty) (creg l ++ " = ")
328335 (fn ++ "(" ++ showSep "," (map fcall args) ++ ")") ++ ";\n"
329− where fcall (t, arg) = irts_c (toFType t) (creg arg)
330336bcc i (NULL r) = indent i ++ creg r ++ " = NULL;\n" -- clear, so it'll be GCed
331337bcc i (ERROR str) = indent i ++ "fprintf(stderr, " ++ show str ++ "); fprintf(stderr, \"\\n\"); exit(-1);\n"
332338-- bcc i c = error (show c) -- indent i ++ "// not done yet\n"
333339
340+fcall (t, arg) = irts_c (toFType t) (creg arg)
334341-- Deconstruct the Foreign type in the defunctionalised expression and build
335342-- a foreign type description for c_irts and irts_c
336343toAType (FCon i)
@@ -388,6 +395,10 @@ irts_c FAny x = x
388395irts_c FFunctionIO x = wrapped x
389396irts_c FFunction x = wrapped x
390397
398+cFnSig name rty [] = ctype rty ++ " (*" ++ name ++ ")(void) "
399+cFnSig name rty args = ctype rty ++ " (*" ++ name ++ ")("
400+ ++ showSep "," (map (ctype . fst) args) ++ ") "
401+
391402wrapped x = "_idris_get_wrapper(" ++ x ++ ")"
392403
393404bitOp v op ty args = v ++ "idris_b" ++ show (nativeTyWidth ty) ++ op ++ "(vm, " ++ intercalate ", " (map creg args) ++ ")"
@@ -632,6 +643,12 @@ doOp v (LExternal pk) [_, p, o] | pk == sUN "prim__peek64"
632643 = v ++ "idris_peekB64(vm," ++ creg p ++ "," ++ creg o ++")"
633644doOp v (LExternal pk) [_, p, o, x] | pk == sUN "prim__poke64"
634645 = v ++ "idris_pokeB64(" ++ creg p ++ "," ++ creg o ++ "," ++ creg x ++ ")"
646+doOp v (LExternal pk) [_, p, o] | pk == sUN "prim__peekPtr"
647+ = v ++ "idris_peekPtr(vm," ++ creg p ++ "," ++ creg o ++")"
648+doOp v (LExternal pk) [_, p, o, x] | pk == sUN "prim__pokePtr"
649+ = v ++ "idris_pokePtr(" ++ creg p ++ "," ++ creg o ++ "," ++ creg x ++ ")"
650+doOp v (LExternal pk) [] | pk == sUN "prim__sizeofPtr"
651+ = v ++ "MKINT(sizeof(void*))"
635652doOp v (LExternal mpt) [p] | mpt == sUN "prim__asPtr" = v ++ "MKPTR(vm, GETMPTR("++ creg p ++"))"
636653doOp _ op args = error $ "doOp not implemented (" ++ show (op, args) ++ ")"
637654
@@ -786,8 +803,8 @@ genWrapper (desc, tag) = ret ++ " " ++ wrapperName tag ++ "(" ++
786803 indent 1 ++ "CALL(_idris__123_APPLY0_125_);\n" ++
787804 if ret /= "void"
788805 then indent 1 ++ "ret = " ++ irts_c (toFType ft) "RVAL" ++ ";\n"
789− ++ indent 1 ++ "return ret;\n}"
790− else "}\n"
806+ ++ indent 1 ++ "return ret;\n}\n\n"
807+ else "}\n\n"
791808 where
792809 (ret, ft) = rty desc
793810 argList = zip (args desc) [0..]
test/ffi007/expected+3
@@ -3,3 +3,6 @@
33Before calling callback
44In an Idris callback
55After calling callback
6+00000377
7+I'm dynamic 3
8+6
test/ffi007/ffi007.c+9
@@ -19,3 +19,12 @@ void test_ffi3(void (*cb)(void))
1919 cb();
2020 printf("After calling callback\n");
2121}
22+
23+int dynamic_fn(int i) {
24+ printf("I'm dynamic %d\n", i);
25+ return i*2;
26+}
27+
28+callback test_ffi6(void) {
29+ return &dynamic_fn;
30+}
test/ffi007/ffi007.h+7
@@ -1,4 +1,11 @@
1+#include <stdint.h>
2+typedef int (*callback)(int);
3+
4+int32_t testvar = 887;
5+
16void test_ffi(int (*cb)(int));
27
38void test_ffi2(int (*cb)(char*));
49void test_ffi3(void (*cb)(void));
10+
11+callback test_ffi6(void);
test/ffi007/ffi007.idr+15−1
@@ -23,8 +23,16 @@ test3 : IO ()
2323test3 = foreign FFI_C "test_ffi3" (CFnPtr (() -> ()) -> IO ()) (MkCFnPtr printIt)
2424
2525test4 : IO Ptr
26−test4 = foreign FFI_C "_idris_get_wrapper" (CFnPtr (() -> ()) -> IO Ptr) (MkCFnPtr printIt)
26+test4 = foreign FFI_C "%wrapper" (CFnPtr (() -> ()) -> IO Ptr) (MkCFnPtr printIt)
2727
28+test5 : IO Ptr
29+test5 = foreign FFI_C "&testvar" (IO Ptr)
30+
31+test6 : IO Ptr
32+test6 = foreign FFI_C "test_ffi6" (IO Ptr)
33+
34+test7 : Ptr -> Int -> IO Int
35+test7 fnptr i = foreign FFI_C "%dynamic" (Ptr -> Int -> IO Int) fnptr i
2836
2937main : IO ()
3038main = do
@@ -32,4 +40,10 @@ main = do
3240 test2
3341 test3
3442 ptr <- test4
43+ tv <- test5
44+ val <- prim_peek32 tv 0
45+ printLn val
46+ fptr <- test6
47+ i <- test7 fptr 3
48+ printLn i
3549 return ()