Skip to content

Commit 722b464

Browse files
authored
Merge pull request #6060 from unisonweb/topic/ffi-types
2 parents a6527ab + 3180e4b commit 722b464

File tree

28 files changed

+1154
-879
lines changed

28 files changed

+1154
-879
lines changed

parser-typechecker/src/Unison/Builtin.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,9 +815,14 @@ builtinsSrc =
815815
B "Natural.isEven" $ natural --> boolean,
816816
B "Natural.isOdd" $ natural --> boolean,
817817
B "FFI.openDLL" $ text --> ioexn dll,
818+
B "FFI.int16" $ ffiType int,
819+
B "FFI.int32" $ ffiType int,
818820
B "FFI.int64" $ ffiType int,
819821
B "FFI.uint64" $ ffiType nat,
822+
B "FFI.uint32" $ ffiType nat,
823+
B "FFI.uint16" $ ffiType nat,
820824
B "FFI.double" $ ffiType float,
825+
B "FFI.float" $ ffiType float,
821826
B "FFI.void" $ ffiType unit,
822827
B "FFI.base" . forall2 "a" "b" $ \a b ->
823828
ffiType a --> ffiType b --> ffiSpec (a --> Type.effect () [] b),

unison-runtime/src/Unison/Runtime/Builtin.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,8 +1406,13 @@ declareForeigns = do
14061406
declareForeign Untracked 3 Avro_decodeBinary
14071407
declareForeign Tracked 1 FFI_openDLL
14081408
declareForeignWrap Untracked direct FFI_int64
1409+
declareForeignWrap Untracked direct FFI_int32
1410+
declareForeignWrap Untracked direct FFI_int16
14091411
declareForeignWrap Untracked direct FFI_uint64
1412+
declareForeignWrap Untracked direct FFI_uint32
1413+
declareForeignWrap Untracked direct FFI_uint16
14101414
declareForeignWrap Untracked direct FFI_double
1415+
declareForeignWrap Untracked direct FFI_float
14111416
declareForeignWrap Untracked direct FFI_void
14121417
declareForeign Untracked 2 FFI_base
14131418
declareForeign Untracked 2 FFI_baseIO

unison-runtime/src/Unison/Runtime/Foreign/Dynamic.hs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,40 @@ import Unison.Runtime.FFI.DLL
1515
import Unison.Runtime.Foreign
1616
import Unison.Type (ffiFuncRef, ffiSpecRef, ffiTypeRef)
1717

18-
data FFType = I64 | U64 | D64 | Void
18+
data FFType = I16 | I32 | I64 | U16 | U32 | U64 | F32 | D64 | Void
1919
deriving (Eq, Ord, Show)
2020

2121
instance BuiltinForeign FFType where
2222
foreignName = Tagged "FFI.Type"
2323
foreignRef = Tagged ffiTypeRef
2424

2525
-- arguments and return type
26-
data FFSpec = FFSpec [FFType] !FFType deriving (Eq, Ord, Show)
27-
28-
ffArgs :: FFSpec -> [FFType]
29-
ffArgs (FFSpec as _) = as
26+
data FFSpec = FFSpec {ffArgs :: ![FFType], ffResult :: !FFType}
27+
deriving (Eq, Ord, Show)
3028

3129
instance BuiltinForeign FFSpec where
3230
foreignName = Tagged "FFI.Spec"
3331
foreignRef = Tagged ffiSpecRef
3432

3533
data CSpec = CSpec
3634
{ cInterface :: !(ForeignPtr CIF),
37-
numArgs :: !Int
35+
numArgs :: !Int,
36+
ffSpec :: !FFSpec
3837
}
3938

4039
data CDynFunc = forall a.
4140
CDynFunc
4241
{ cName :: String,
43-
cResult :: !FFType,
4442
cSpec :: {-# UNPACK #-} !CSpec,
4543
cFun :: !(FunPtr a)
4644
}
4745

46+
cffArgs :: CDynFunc -> [FFType]
47+
cffArgs = ffArgs . ffSpec . cSpec
48+
49+
cffResult :: CDynFunc -> FFType
50+
cffResult = ffResult . ffSpec . cSpec
51+
4852
instance Show CDynFunc where
4953
show f = "<" ++ cName f ++ ">"
5054

@@ -53,9 +57,14 @@ instance BuiltinForeign CDynFunc where
5357
foreignRef = Tagged ffiFuncRef
5458

5559
encodeType :: FFType -> Ptr CType
60+
encodeType I16 = ffi_type_sint16
61+
encodeType I32 = ffi_type_sint32
5662
encodeType I64 = ffi_type_sint64
63+
encodeType U16 = ffi_type_uint16
64+
encodeType U32 = ffi_type_uint32
5765
encodeType U64 = ffi_type_uint64
5866
encodeType D64 = ffi_type_double
67+
encodeType F32 = ffi_type_float
5968
encodeType Void = ffi_type_void
6069

6170
encodeTypes :: [FFType] -> Ptr (Ptr CType) -> IO ()
@@ -78,7 +87,7 @@ adjustSpec sp@(FFSpec as r)
7887

7988
prepareSpec :: FFSpec -> IO CSpec
8089
prepareSpec spec = do
81-
FFSpec args ret <- adjustSpec spec
90+
ffSpec@(FFSpec args ret) <- adjustSpec spec
8291
let numArgs = length args
8392
n = fromIntegral numArgs
8493

@@ -91,11 +100,11 @@ prepareSpec spec = do
91100
unless (status == ffi_ok) $
92101
throwIO BadInit
93102

94-
pure $ CSpec {cInterface, numArgs}
103+
pure $ CSpec {cInterface, numArgs, ffSpec}
95104

96105
loadForeign :: DLL -> FFSpec -> String -> IO CDynFunc
97-
loadForeign dll fspec@(FFSpec _ r) sym =
98-
CDynFunc name r <$> prepareSpec fspec <*> getDLLSym dll sym
106+
loadForeign dll fspec sym =
107+
CDynFunc name <$> prepareSpec fspec <*> getDLLSym dll sym
99108
where
100109
name = getDLLPath dll ++ "$" ++ sym
101110

@@ -112,6 +121,6 @@ loadForeign dll fspec@(FFSpec _ r) sym =
112121
--
113122
-- Store.poke (castPtr (plusPtr p i)) <smaller-value>
114123
callForeign :: CDynFunc -> Ptr (Ptr a) -> Ptr r -> IO ()
115-
callForeign (CDynFunc _ _ (CSpec cInterface _) fun) cArgs cRet =
124+
callForeign (CDynFunc _ (CSpec cInterface _ _) fun) cArgs cRet =
116125
withForeignPtr cInterface \cif ->
117126
ffi_call cif fun (castPtr cRet) (castPtr cArgs)

unison-runtime/src/Unison/Runtime/Foreign/Function.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,8 +1160,13 @@ foreignCallHelper = \case
11601160
FFI_openDLL -> mkForeignIOExn $ \(fname :: Text) ->
11611161
evaluate =<< openDLL (unpack fname)
11621162
FFI_int64 -> mkForeign \() -> pure $ I64
1163+
FFI_int32 -> mkForeign \() -> pure $ I32
1164+
FFI_int16 -> mkForeign \() -> pure $ I16
11631165
FFI_uint64 -> mkForeign \() -> pure $ U64
1166+
FFI_uint32 -> mkForeign \() -> pure $ U32
1167+
FFI_uint16 -> mkForeign \() -> pure $ U16
11641168
FFI_double -> mkForeign \() -> pure $ D64
1169+
FFI_float -> mkForeign \() -> pure $ F32
11651170
FFI_void -> mkForeign \() -> pure $ Void
11661171
FFI_base -> mkForeign $ \(a, r) -> evaluate $ FFSpec [a] r
11671172
FFI_baseIO -> mkForeign $ \(a, r) -> evaluate $ FFSpec [a] r

unison-runtime/src/Unison/Runtime/Foreign/Function/Type.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,13 @@ data ForeignFunc
368368
| Universal_murmurHashUntyped
369369
| FFI_openDLL
370370
| FFI_int64
371+
| FFI_int32
372+
| FFI_int16
371373
| FFI_uint64
374+
| FFI_uint32
375+
| FFI_uint16
372376
| FFI_double
377+
| FFI_float
373378
| FFI_void
374379
| FFI_base
375380
| FFI_baseIO
@@ -739,8 +744,13 @@ foreignFuncBuiltinName = \case
739744
Universal_murmurHashUntyped -> "Universal.murmurHashUntyped"
740745
FFI_openDLL -> "FFI.openDLL"
741746
FFI_int64 -> "FFI.int64"
747+
FFI_int32 -> "FFI.int32"
748+
FFI_int16 -> "FFI.int16"
742749
FFI_uint64 -> "FFI.uint64"
750+
FFI_uint32 -> "FFI.uint32"
751+
FFI_uint16 -> "FFI.uint16"
743752
FFI_double -> "FFI.double"
753+
FFI_float -> "FFI.float"
744754
FFI_void -> "FFI.void"
745755
FFI_base -> "FFI.base"
746756
FFI_baseIO -> "FFI.baseIO"

unison-runtime/src/Unison/Runtime/Machine.hs

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import Foreign.Marshal.Array (allocaArray)
4848
import Foreign.Ptr
4949
import Foreign.Storable qualified as Store
5050
import GHC.Conc as STM (unsafeIOToSTM)
51+
import GHC.Float (double2Float, float2Double)
5152
import GHC.Stack
5253
import Unison.Builtin.Decls (exceptionRef)
5354
import Unison.Builtin.Decls qualified as Rf
@@ -490,33 +491,79 @@ exec _ henv !_activeThreads !stk !k _ DLLCall = do
490491
allocaArray n \storage ->
491492
allocaArray n \cArgs ->
492493
alloca \(cRet :: Ptr Int) -> do
493-
copyArgs stk n storage cArgs
494+
copyArgs stk (DLL.cffArgs cf) storage cArgs
494495
DLL.callForeign cf cArgs cRet
495-
case DLL.cResult cf of
496+
case DLL.cffResult cf of
497+
DLL.I16 -> Store.peek (castPtr cRet) >>= pokeI stk . fi16
498+
DLL.I32 -> Store.peek (castPtr cRet) >>= pokeI stk . fi32
496499
DLL.I64 -> Store.peek cRet >>= pokeI stk
500+
DLL.U16 -> Store.peek (castPtr cRet) >>= pokeN stk . fu16
501+
DLL.U32 -> Store.peek (castPtr cRet) >>= pokeN stk . fu32
497502
DLL.U64 -> Store.peek (castPtr cRet) >>= pokeN stk
503+
DLL.F32 -> Store.peek (castPtr cRet) >>= pokeD stk . ff32
498504
DLL.D64 -> Store.peek (castPtr cRet) >>= pokeD stk
499505
DLL.Void -> poke stk unitValue
500506
pure (False, henv, stk, k)
501507
exec _ _ !_ !_ !_ _ (SandboxingFailure t) = do
502508
die [] $ "Attempted to use disallowed builtin in sandboxed environment: " <> DTx.unpack t
503509
{-# INLINE exec #-}
504510

511+
fi16 :: Int16 -> Int
512+
fi16 = fromIntegral
513+
514+
ti16 :: Int -> Int16
515+
ti16 = fromIntegral
516+
517+
fi32 :: Int32 -> Int
518+
fi32 = fromIntegral
519+
520+
ti32 :: Int -> Int32
521+
ti32 = fromIntegral
522+
523+
fu32 :: Word32 -> Word64
524+
fu32 = fromIntegral
525+
526+
tu32 :: Word64 -> Word32
527+
tu32 = fromIntegral
528+
529+
fu16 :: Word16 -> Word64
530+
fu16 = fromIntegral
531+
532+
tu16 :: Word64 -> Word16
533+
tu16 = fromIntegral
534+
535+
tf32 :: Double -> Float
536+
tf32 = double2Float
537+
538+
ff32 :: Float -> Double
539+
ff32 = float2Double
540+
505541
-- Copies unison stack values into temporary space appropriate for
506542
-- calling libffi. The latter takes all arguments as pointers, so we
507543
-- need to copy the arguments to pinned memory to have a stable
508-
-- location. All our FFI arguments are 64-bit, though, so we can just
509-
-- use a contiguous array.
510-
copyArgs :: Stack -> Int -> Ptr Int -> Ptr (Ptr CValue) -> IO ()
511-
copyArgs !stk n = go 2
544+
-- location. All our FFI arguments are 64-bit or smaller, though, so
545+
-- we can just use a contiguous array with as many 8 byte slots as
546+
-- there are arguments, possibly using only portions of some slots.
547+
copyArgs ::
548+
Stack -> [DLL.FFType] -> Ptr Int -> Ptr (Ptr CValue) -> IO ()
549+
copyArgs !stk = go 2
512550
where
513-
go i !p !h
514-
| i <= n + 1 = do
515-
k <- upeekOff stk i
516-
Store.poke p k
517-
Store.poke h (castPtr p)
518-
go (i + 1) (plusPtr p szp) (plusPtr h szh)
519-
| otherwise = pure ()
551+
go !i (a : as) !p !h = do
552+
store a i p
553+
Store.poke h (castPtr p)
554+
go (i + 1) as (plusPtr p szp) (plusPtr h szh)
555+
go _ _ _ _ = pure ()
556+
557+
-- special case non-64-bit values for conversions, otherwise just
558+
-- copy bytes.
559+
store DLL.I32 i p = upeekOff stk i >>= Store.poke (castPtr p) . ti32
560+
store DLL.U32 i p = peekOffN stk i >>= Store.poke (castPtr p) . tu32
561+
store DLL.I16 i p = upeekOff stk i >>= Store.poke (castPtr p) . ti16
562+
store DLL.U16 i p = peekOffN stk i >>= Store.poke (castPtr p) . tu16
563+
store DLL.F32 i p = peekOffD stk i >>= Store.poke (castPtr p) . tf32
564+
store _ i p = upeekOff stk i >>= Store.poke p
565+
{-# INLINE store #-}
566+
520567
szp = Store.sizeOf (0 :: Int)
521568
szh = Store.sizeOf (undefined :: Ptr CValue)
522569
{-# INLINE copyArgs #-}

unison-src/transcripts-manual/dll-ffi-unix.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,32 @@ scratch/dll-ffi> builtins.mergeio
33
```
44

55
``` unison
6-
testSpec = arr int64 (base int64 int64)
6+
testu64Spec = arr uint64 (base uint64 uint64)
7+
testu32Spec = arr uint32 (base uint32 uint32)
8+
testu16Spec = arr uint16 (base uint16 uint16)
9+
testi64Spec = arr int64 (base int64 int64)
10+
testi32Spec = arr int32 (base int32 int32)
11+
testi16Spec = arr int16 (base int16 int16)
12+
testdSpec = arr double (base double double)
13+
testfSpec = arr float (base float float)
14+
15+
716
libtest = do openDLL "unison-src/transcripts-manual/dll-ffi/libtest.so"
817
918
doTest = do
1019
dll = libtest()
11-
f = getDLLSym dll "test" testSpec
12-
f +1 +2
20+
tu64 = getDLLSym dll "testu64" testu64Spec
21+
tu32 = getDLLSym dll "testu32" testu32Spec
22+
tu16 = getDLLSym dll "testu16" testu16Spec
23+
ti64 = getDLLSym dll "testi64" testi64Spec
24+
ti32 = getDLLSym dll "testi32" testi32Spec
25+
ti16 = getDLLSym dll "testi16" testi16Spec
26+
td = getDLLSym dll "testd" testdSpec
27+
tf = getDLLSym dll "testf" testfSpec
28+
( tu64 1 2, tu32 1 2, tu16 1 2
29+
, ti64 +1 +2, ti32 +1 +2, ti16 +1 +2
30+
, td 1.0 2.0, tf 1.0 2.0
31+
)
1332
```
1433

1534
``` ucm

unison-src/transcripts-manual/dll-ffi-unix.output.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,60 @@ scratch/dll-ffi> builtins.mergeio
33
```
44

55
``` unison
6-
testSpec = arr int64 (base int64 int64)
6+
testu64Spec = arr uint64 (base uint64 uint64)
7+
testu32Spec = arr uint32 (base uint32 uint32)
8+
testu16Spec = arr uint16 (base uint16 uint16)
9+
testi64Spec = arr int64 (base int64 int64)
10+
testi32Spec = arr int32 (base int32 int32)
11+
testi16Spec = arr int16 (base int16 int16)
12+
testdSpec = arr double (base double double)
13+
testfSpec = arr float (base float float)
14+
15+
716
libtest = do openDLL "unison-src/transcripts-manual/dll-ffi/libtest.so"
817
918
doTest = do
1019
dll = libtest()
11-
f = getDLLSym dll "test" testSpec
12-
f +1 +2
20+
tu64 = getDLLSym dll "testu64" testu64Spec
21+
tu32 = getDLLSym dll "testu32" testu32Spec
22+
tu16 = getDLLSym dll "testu16" testu16Spec
23+
ti64 = getDLLSym dll "testi64" testi64Spec
24+
ti32 = getDLLSym dll "testi32" testi32Spec
25+
ti16 = getDLLSym dll "testi16" testi16Spec
26+
td = getDLLSym dll "testd" testdSpec
27+
tf = getDLLSym dll "testf" testfSpec
28+
( tu64 1 2, tu32 1 2, tu16 1 2
29+
, ti64 +1 +2, ti32 +1 +2, ti16 +1 +2
30+
, td 1.0 2.0, tf 1.0 2.0
31+
)
1332
```
1433

1534
``` ucm :added-by-ucm
1635
Loading changes detected in scratch.u.
1736
18-
+ doTest : '{IO, Exception} Int
19-
+ libtest : '{IO, Exception} DLL
20-
+ testSpec : Spec (Int -> Int -> Int)
37+
+ doTest : '{IO, Exception} ( Nat,
38+
Nat,
39+
Nat,
40+
Int,
41+
Int,
42+
Int,
43+
Float,
44+
Float)
45+
+ libtest : '{IO, Exception} DLL
46+
+ testdSpec : Spec (Float -> Float -> Float)
47+
+ testfSpec : Spec (Float -> Float -> Float)
48+
+ testi16Spec : Spec (Int -> Int -> Int)
49+
+ testi32Spec : Spec (Int -> Int -> Int)
50+
+ testi64Spec : Spec (Int -> Int -> Int)
51+
+ testu16Spec : Spec (Nat -> Nat -> Nat)
52+
+ testu32Spec : Spec (Nat -> Nat -> Nat)
53+
+ testu64Spec : Spec (Nat -> Nat -> Nat)
2154
2255
Run `update` to apply these changes to your codebase.
2356
```
2457

2558
``` ucm
2659
scratch/dll-ffi> run doTest
2760
28-
+4
61+
(4, 4, 4, +4, +4, +4, 4.0, 4.0)
2962
```

0 commit comments

Comments
 (0)