on master help sign in
Merge pull request #2972 from melted/cffi

A higher level C FFI

commit2052c187b1cab4c201517d929bbdcce84675e039 authorNiklas Larsson <metaniklas@gmail.com> dateMon 7 Mar 2016, 17:08 (11 years ago) parent504187b30e5a19
16 files changed+400−2
idris.cabal+7 libs/contrib/CFFI.idr+4 libs/contrib/CFFI/Memory.idr+90 libs/contrib/CFFI/Types.idr+159 libs/contrib/contrib.ipkg+3−1 libs/prelude/Builtins.idr+5 libs/prelude/IO.idr+16 rts/idris_rts.c+18 rts/idris_rts.h+4 src/IRTS/CodegenC.hs+8 test/Makefile+1−1 test/ffi008/expected+5 test/ffi008/ffi008.c+14 test/ffi008/ffi008.h+17 test/ffi008/ffi008.idr+45 test/ffi008/run+4
idris.cabal+7
@@ -124,6 +124,8 @@ Extra-source-files:
124124
125125 libs/contrib/contrib.ipkg
126126 libs/contrib/Makefile
127+ libs/contrib/*.idr
128+ libs/contrib/CFFI/*.idr
127129 libs/contrib/Classes/*.idr
128130 libs/contrib/Control/*.idr
129131 libs/contrib/Control/Isomorphism/*.idr
@@ -482,6 +484,11 @@ Extra-source-files:
482484 test/ffi007/*.h
483485 test/ffi007/run
484486 test/ffi007/expected
487+ test/ffi008/*.idr
488+ test/ffi008/*.c
489+ test/ffi008/*.h
490+ test/ffi008/run
491+ test/ffi008/expected
485492
486493 test/folding001/*.idr
487494 test/folding001/run
libs/contrib/CFFI.idrnew file+4
@@ -0,0 +1,4 @@
1+module CFFI
2+
3+import public CFFI.Types
4+import public CFFI.Memory
libs/contrib/CFFI/Memory.idrnew file+90
@@ -0,0 +1,90 @@
1+||| Machinery for interfacing with C.
2+module CFFI.Memory
3+
4+import CFFI.Types
5+import Data.Vect
6+
7+%include C "memory.h"
8+
9+%access public export
10+%default partial
11+
12+
13+data CPtr = CPt Ptr Int
14+
15+implicit toCPtr : Ptr -> CPtr
16+toCPtr p = CPt p 0
17+
18+||| Import of malloc from the C standard library.
19+malloc : Int -> IO Ptr
20+malloc size = foreign FFI_C "malloc" (Int -> IO Ptr) size
21+
22+||| Import of free from the C standard library.
23+mfree : Ptr -> IO ()
24+mfree ptr = foreign FFI_C "free" (Ptr -> IO ()) ptr
25+
26+||| Allocate enough memory to hold an instance of a C typr
27+alloc : Composite -> IO CPtr
28+alloc t = return $ CPt !(malloc (sizeOf t)) 0
29+
30+||| Free memory allocated with alloc
31+free : CPtr -> IO ()
32+free (CPt p _ ) = mfree p
33+
34+||| Perform an IO action with memory that is freed afterwards
35+withAlloc : Composite -> (CPtr -> IO ()) -> IO ()
36+withAlloc t f = do m <- alloc t
37+ f m
38+ free m
39+
40+infixl 1 ~~>
41+||| Perform an IO action with memory that is freed afterwards
42+(~~>) : Composite-> (CPtr -> IO ()) -> IO ()
43+(~~>) = withAlloc
44+
45+||| Read from memory
46+peek : (t: CType) -> CPtr -> IO (translate t)
47+peek I8 (CPt p o) = prim_peek8 p o
48+peek I16 (CPt p o) = prim_peek16 p o
49+peek I32 (CPt p o) = prim_peek32 p o
50+peek I64 (CPt p o) = prim_peek64 p o
51+peek FLOAT (CPt p o) = prim_peekSingle p o
52+peek DOUBLE (CPt p o) = prim_peekDouble p o
53+peek PTR (CPt p o) = prim_peekPtr p o
54+
55+||| Write to memory
56+poke : (t : CType) -> CPtr -> translate t -> IO ()
57+poke I8 (CPt p o) x = do _ <- prim_poke8 p o x
58+ return ()
59+poke I16 (CPt p o) x = do _ <- prim_poke16 p o x
60+ return ()
61+poke I32 (CPt p o) x = do _ <- prim_poke32 p o x
62+ return ()
63+poke I64 (CPt p o) x = do _ <- prim_poke64 p o x
64+ return ()
65+poke PTR (CPt p o) x = do _ <- prim_pokePtr p o x
66+ return ()
67+poke FLOAT (CPt p o) x = do _ <- prim_pokeSingle p o x
68+ return ()
69+poke DOUBLE (CPt p o) x = do _ <- prim_pokeDouble p o x
70+ return ()
71+
72+||| Update memory with a function.
73+update : (t: CType) -> CPtr -> (translate t -> translate t) -> IO ()
74+update ty cp f = do val <- peek ty cp
75+ out <- return $ f val
76+ poke ty cp out
77+
78+||| Get a pointer to a field in a composite value
79+field : Composite -> Nat -> CPtr -> CPtr
80+field arr@(ARRAY n t) i (CPt p o ) = CPt p (o + offset arr i)
81+field un@(UNION xs) i (CPt p o ) = CPt p o
82+field st@(STRUCT xs) i (CPt p o ) = CPt p (o + offset st i)
83+field ps@(PACKEDSTRUCT xs) i (CPt p o ) = CPt p (o + offset ps i)
84+field (T t) Z p = p
85+
86+infixl 10 #
87+
88+||| Get a pointer to a field in a composite value
89+(#) : Composite -> Nat -> CPtr -> CPtr
90+(#) = field
libs/contrib/CFFI/Types.idrnew file+159
@@ -0,0 +1,159 @@
1+||| Types for interfacing with C.
2+||| This file should be kept free from IO.
3+module CFFI.Types
4+
5+import Data.Vect
6+import Debug.Error
7+
8+%access public export
9+%default partial
10+
11+||| An universe of C types.
12+data CType = I8 | I16 | I32 | I64 | FLOAT | DOUBLE | PTR
13+
14+||| Composites of C types
15+data Composite = T CType | ARRAY Int Composite | STRUCT (List Composite) | UNION (List Composite)
16+ | PACKEDSTRUCT (List Composite)
17+
18+||| Implicit conversion of primitive C types to composites
19+implicit mkComposite : CType -> Composite
20+mkComposite x = T x
21+
22+Show CType where
23+ show I8 = "I8"
24+ show I16 = "I16"
25+ show I32 = "I32"
26+ show I64 = "I64"
27+ show FLOAT = "FLOAT"
28+ show DOUBLE = "DOUBLE"
29+ show PTR = "PTR"
30+
31+Show Composite where
32+ show (T ct) = show ct
33+ show (ARRAY n t) = "ARRAY " ++ show n ++ " " ++ show t
34+ show (STRUCT xs) = "STRUCT " ++ show xs
35+ show (UNION xs) = "UNION " ++ show xs
36+ show (PACKEDSTRUCT xs) = "PACKEDSTRUCT " ++ show xs
37+
38+||| What Idris type the C type is marshalled to
39+translate : CType -> Type
40+translate I8 = Bits8
41+translate I16 = Bits16
42+translate I32 = Bits32
43+translate I64 = Bits64
44+translate FLOAT = Double
45+translate DOUBLE = Double
46+translate PTR = Ptr
47+
48+mutual
49+ private
50+ sizeOfCT : CType -> Int
51+ sizeOfCT I8 = 1
52+ sizeOfCT I16 = 2
53+ sizeOfCT I32 = 4
54+ sizeOfCT I64 = 8
55+ sizeOfCT FLOAT = 4
56+ sizeOfCT DOUBLE = 8
57+ sizeOfCT PTR = prim__sizeofPtr
58+
59+ ||| Size of value of the type in bytes
60+ export
61+ sizeOf : Composite -> Int
62+ sizeOf (T ct) = sizeOfCT ct
63+ sizeOf (ARRAY n t) = n * sizeOf t
64+ sizeOf (STRUCT xs) = sizeOfStruct xs
65+ sizeOf (UNION xs) = foldl (\acc, x =>max acc $ sizeOf x) 0 xs
66+ sizeOf (PACKEDSTRUCT xs) = foldl (\acc, x => acc + sizeOf x) 0 xs
67+
68+ private
69+ alignOfCT : CType -> Int
70+ alignOfCT I8 = 1
71+ alignOfCT I16 = 2
72+ alignOfCT I32 = 4
73+ alignOfCT I64 = prim__sizeofPtr
74+ alignOfCT FLOAT = 4
75+ alignOfCT DOUBLE = prim__sizeofPtr
76+ alignOfCT PTR = prim__sizeofPtr
77+
78+ ||| Alignment requirement of the type
79+ export
80+ alignOf : Composite -> Int
81+ alignOf (T t) = alignOfCT t
82+ alignOf (ARRAY n t) = alignOf t
83+ alignOf (STRUCT xs) = foldl (\acc, x => max acc $ alignOf x) 0 xs
84+ alignOf (UNION xs) = foldl (\acc, x => max acc $ alignOf x) 0 xs
85+ alignOf (PACKEDSTRUCT xs) = 1
86+
87+ private
88+ pad : Int -> Int -> Int
89+ pad pos align = let c = pos `mod` align in if c == 0 then 0 else align - c
90+
91+ private
92+ nextOffset : Composite -> Int -> Int
93+ nextOffset ty pos = pos + pad pos (alignOf ty)
94+
95+ private
96+ offsetsStruct : List Composite -> List Int
97+ offsetsStruct xs = offsets' xs 0
98+ where
99+ offsets' : List Composite -> Int -> List Int
100+ offsets' [] _ = []
101+ offsets' (x::xs) pos = (nextOffset x pos)::(offsets' xs (nextOffset x pos + sizeOf x))
102+
103+ private
104+ sizeOfStruct : List Composite -> Int
105+ sizeOfStruct xs = sizeStruct' xs 0 1
106+ where
107+ sizeStruct' : List Composite -> Int -> Int -> Int
108+ sizeStruct' [] pos maxAlign = pos + (pad pos maxAlign)
109+ sizeStruct' (x::xs) pos maxAlign = sizeStruct' xs (nextOffset x pos + sizeOf x)
110+ (max maxAlign (alignOf x))
111+
112+||| Number of fields in a composite type
113+export
114+fields : Composite -> Nat
115+fields (STRUCT xs) = length xs
116+fields (PACKEDSTRUCT xs) = length xs
117+fields (UNION xs) = length xs
118+fields (ARRAY n _) = toNat n
119+fields (T _) = 1
120+
121+private
122+offsetsPacked : List Composite -> List Int
123+offsetsPacked xs = offsets' xs [] 0
124+ where
125+ offsets' : List Composite -> List Int -> Int -> List Int
126+ offsets' [] acc _ = reverse acc
127+ offsets' (x::xs) acc pos = offsets' xs (pos::acc) (sizeOf x)
128+
129+private
130+indexOrFail : Nat -> List a -> a
131+indexOrFail i xs = case index' i xs of
132+ Just x => x
133+ Nothing => error "Out of bounds access"
134+
135+||| The offset of a firld in a composite type
136+export
137+offset : Composite -> Nat -> Int
138+offset (STRUCT xs) i = indexOrFail i (offsetsStruct xs)
139+offset (PACKEDSTRUCT xs) i = indexOrFail i (offsetsPacked xs)
140+offset (ARRAY _ t) i = sizeOf t * toIntNat i
141+offset (T _) _ = 0
142+
143+||| All offsets of a composite type
144+export
145+offsets : Composite -> List Int
146+offsets (STRUCT xs) = offsetsStruct xs
147+offsets (PACKEDSTRUCT xs) = offsetsPacked xs
148+offsets (UNION xs) = replicate (length xs) 0
149+offsets (ARRAY n t) = [ x*sizeOf t | x <- [0..n-1]]
150+offsets (T _) = [0]
151+
152+||| The type of a field in a composite type.
153+export
154+fieldType : Composite -> Nat -> Composite
155+fieldType (STRUCT xs@(y::_)) i = indexOrFail i xs
156+fieldType (PACKEDSTRUCT xs@(y::_)) i = indexOrFail i xs
157+fieldType (UNION xs@(y::_)) i = indexOrFail i xs
158+fieldType (ARRAY n t) _ = t
159+fieldType t _ = t
libs/contrib/contrib.ipkg+3−1
@@ -1,7 +1,9 @@
11package contrib
22
33opts = "--nobasepkgs --total -i ../prelude -i ../base"
4−modules = Control.Algebra,
4+modules = CFFI, CFFI.Types, CFFI.Memory,
5+
6+ Control.Algebra,
57 Control.Algebra.Lattice, Control.Algebra.VectorSpace,
68 Control.Algebra.NumericInstances,
79 Control.Isomorphism.Primitives,
libs/prelude/Builtins.idr+5
@@ -214,3 +214,8 @@ export data CData : Type
214214
215215%extern prim__peekPtr : prim__WorldType -> Ptr -> Int -> Ptr
216216%extern prim__pokePtr : prim__WorldType -> Ptr -> Int -> Ptr -> Int
217+
218+%extern prim__peekDouble : prim__WorldType -> Ptr -> Int -> Double
219+%extern prim__pokeDouble : prim__WorldType -> Ptr -> Int -> Double -> Int
220+%extern prim__peekSingle : prim__WorldType -> Ptr -> Int -> Double
221+%extern prim__pokeSingle : prim__WorldType -> Ptr -> Int -> Double -> Int
libs/prelude/IO.idr+16
@@ -349,3 +349,19 @@ prim_peekPtr ptr offset = MkIO (\w => prim_io_return (prim__peekPtr (world w) pt
349349prim_pokePtr : Ptr -> Int -> Ptr -> IO Int
350350prim_pokePtr ptr offset val = MkIO (\w => prim_io_return (
351351 prim__pokePtr (world w) ptr offset val))
352+
353+prim_peekDouble : Ptr -> Int -> IO Double
354+prim_peekDouble ptr offset = MkIO (\w => prim_io_return (prim__peekDouble (world w) ptr offset))
355+
356+prim_pokeDouble : Ptr -> Int -> Double -> IO Int
357+prim_pokeDouble ptr offset val = MkIO (\w => prim_io_return (
358+ prim__pokeDouble (world w) ptr offset val))
359+
360+||| Single precision floats are marshalled to Doubles
361+prim_peekSingle : Ptr -> Int -> IO Double
362+prim_peekSingle ptr offset = MkIO (\w => prim_io_return (prim__peekSingle (world w) ptr offset))
363+
364+||| Single precision floats are marshalled to Doubles
365+prim_pokeSingle : Ptr -> Int -> Double -> IO Int
366+prim_pokeSingle ptr offset val = MkIO (\w => prim_io_return (
367+ prim__pokeSingle (world w) ptr offset val))
rts/idris_rts.c+18
@@ -464,6 +464,24 @@ VAL idris_pokePtr(VAL ptr, VAL offset, VAL data) {
464464 return MKINT(0);
465465}
466466
467+VAL idris_peekDouble(VM* vm, VAL ptr, VAL offset) {
468+ return MKFLOAT(vm, *(double*)(GETPTR(ptr) + GETINT(offset)));
469+}
470+
471+VAL idris_pokeDouble(VAL ptr, VAL offset, VAL data) {
472+ *(double*)(GETPTR(ptr) + GETINT(offset)) = GETFLOAT(data);
473+ return MKINT(0);
474+}
475+
476+VAL idris_peekSingle(VM* vm, VAL ptr, VAL offset) {
477+ return MKFLOAT(vm, *(float*)(GETPTR(ptr) + GETINT(offset)));
478+}
479+
480+VAL idris_pokeSingle(VAL ptr, VAL offset, VAL data) {
481+ *(float*)(GETPTR(ptr) + GETINT(offset)) = GETFLOAT(data);
482+ return MKINT(0);
483+}
484+
467485void idris_memmove(void* dest, void* src, i_int dest_offset, i_int src_offset, i_int size) {
468486 memmove(dest + dest_offset, src + src_offset, size);
469487}
rts/idris_rts.h+4
@@ -348,6 +348,10 @@ void idris_poke(void* ptr, i_int offset, uint8_t data);
348348
349349VAL idris_peekPtr(VM* vm, VAL ptr, VAL offset);
350350VAL idris_pokePtr(VAL ptr, VAL offset, VAL data);
351+VAL idris_peekDouble(VM* vm, VAL ptr, VAL offset);
352+VAL idris_pokeDouble(VAL ptr, VAL offset, VAL data);
353+VAL idris_peekSingle(VM* vm, VAL ptr, VAL offset);
354+VAL idris_pokeSingle(VAL ptr, VAL offset, VAL data);
351355
352356// String primitives
353357VAL idris_concat(VM* vm, VAL l, VAL r);
src/IRTS/CodegenC.hs+8
@@ -653,6 +653,14 @@ doOp v (LExternal pk) [_, p, o] | pk == sUN "prim__peekPtr"
653653 = v ++ "idris_peekPtr(vm," ++ creg p ++ "," ++ creg o ++")"
654654doOp v (LExternal pk) [_, p, o, x] | pk == sUN "prim__pokePtr"
655655 = v ++ "idris_pokePtr(" ++ creg p ++ "," ++ creg o ++ "," ++ creg x ++ ")"
656+doOp v (LExternal pk) [_, p, o, x] | pk == sUN "prim__pokeDouble"
657+ = v ++ "idris_pokeDouble(" ++ creg p ++ "," ++ creg o ++ "," ++ creg x ++ ")"
658+doOp v (LExternal pk) [_, p, o] | pk == sUN "prim__peekDouble"
659+ = v ++ "idris_peekDouble(vm," ++ creg p ++ "," ++ creg o ++")"
660+doOp v (LExternal pk) [_, p, o, x] | pk == sUN "prim__pokeSingle"
661+ = v ++ "idris_pokeSingle(" ++ creg p ++ "," ++ creg o ++ "," ++ creg x ++ ")"
662+doOp v (LExternal pk) [_, p, o] | pk == sUN "prim__peekSingle"
663+ = v ++ "idris_peekSingle(vm," ++ creg p ++ "," ++ creg o ++")"
656664doOp v (LExternal pk) [] | pk == sUN "prim__sizeofPtr"
657665 = v ++ "MKINT(sizeof(void*))"
658666doOp v (LExternal mpt) [p] | mpt == sUN "prim__asPtr" = v ++ "MKPTR(vm, GETMPTR("++ creg p ++"))"
test/Makefile+1−1
@@ -12,7 +12,7 @@ info: runtest
1212 @./runtest $(patsubst %.test,%,$@) -q
1313
1414test_js: runtest
15− @./runtest without sugar004 reg029 reg052 io001 dsl002 io003 effects001 effects002 basic007 basic011 ffi006 ffi007 primitives005 primitives006 opts --codegen node
15+ @./runtest without sugar004 reg029 reg052 io001 dsl002 io003 effects001 effects002 basic007 basic011 ffi006 ffi007 ffi008 primitives005 primitives006 opts --codegen node
1616
1717update: runtest
1818 @./runtest all -u
test/ffi008/expectednew file+5
@@ -0,0 +1,5 @@
1+True
2+True
3+a: 122 b: 0
4+4
5+00000010
test/ffi008/ffi008.cnew file+14
@@ -0,0 +1,14 @@
1+#include "ffi008.h"
2+#include <stdio.h>
3+
4+int size1(void) {
5+ return sizeof(struct test1);
6+}
7+
8+int size2(void) {
9+ return sizeof(struct test2);
10+}
11+
12+void print_mystruct(void) {
13+ printf("a: %d b: %d\n", mystruct.a, mystruct.b);
14+}
test/ffi008/ffi008.hnew file+17
@@ -0,0 +1,17 @@
1+#include <stdint.h>
2+
3+struct test1 {
4+ int8_t a;
5+ int64_t b;
6+};
7+
8+struct test2 {
9+ int32_t a;
10+ int16_t b;
11+};
12+
13+struct test2 mystruct;
14+
15+int size1(void);
16+int size2(void);
17+void print_mystruct();
test/ffi008/ffi008.idrnew file+45
@@ -0,0 +1,45 @@
1+module Main
2+
3+import CFFI
4+import Data.Vect
5+
6+%include C "ffi008.h"
7+
8+size1 : IO Int
9+size1 = foreign FFI_C "size1" (IO Int)
10+
11+
12+size2 : IO Int
13+size2 = foreign FFI_C "size2" (IO Int)
14+
15+mystruct : IO Ptr
16+mystruct = foreign FFI_C "&mystruct" (IO Ptr)
17+
18+print_mystruct : IO ()
19+print_mystruct = foreign FFI_C "print_mystruct" (IO ())
20+
21+test1 : Composite
22+test1 = STRUCT [I8, I64]
23+
24+test2 : Composite
25+test2 = STRUCT [I32, I16]
26+
27+test3 : Composite
28+test3 = STRUCT [DOUBLE, DOUBLE, I8, I64]
29+
30+test4 : Composite
31+test4 = PACKEDSTRUCT [I8, I8, I8, I64]
32+
33+main : IO ()
34+main = do
35+ printLn $ sizeOf test1 == !size1
36+ printLn $ sizeOf test2 == !size2
37+ fms <- return $ (test2#0) !mystruct
38+ poke I32 fms 122
39+ print_mystruct
40+ printLn $ fields test3
41+ withAlloc test2 $ \p => do
42+ f1 <- return $ (test2 # 0) p
43+ poke I32 f1 8
44+ update I32 f1 (* 2)
45+ print !(peek I32 f1)
test/ffi008/runnew file+4
@@ -0,0 +1,4 @@
1+#!/usr/bin/env bash
2+${IDRIS:-idris} $@ ffi008.idr -o ffi008 -p contrib --cg-opt "ffi008.c"
3+./ffi008
4+rm -f ffi008 *.ibc