cryptol/docs/ffi-presentation.md
2022-09-08 18:28:30 -07:00

5.8 KiB

title author date
Cryptol FFI Bretton 2022-09-02

Cryptol FFI

  • Call functions written in C

Why?

  • Performance
  • Use existing code in C
  • Do things that Cryptol can't do?

What does it look like?

foreign add : [32] -> [32] -> [32]
uint32_t add(uint32_t x, uint32_t y) {
  return x + y;
}
Main> add 1 2
0x00000003

How do you use it?

+-------------+
| Example.cry | ------------------------+       +---------------Cryptol---------------+
+-------------+                         |       | Cryptol> :l Example.cry             |
                                        |       | Loading module Cryptol              |
+-----------+        +---------------+  +------>| Loading module Example              |
| Example.c | =====> | Example.so    | -------->| Loading dynamic library Example.so  |
+-----------+   cc   | Example.dylib |  dlopen  | Example> add 1 2                    |
                     +---------------+          | 0x00000003                          |
                                                +-------------------------------------+
  • Find shared library with same name as Cryptol file
  • Dynamically load with dlopen
  • Bind each foreign function with dlsym
  • Windows not supported yet
  • Compile it yourself (for now...)
    • cc -fPIC -shared Example.c -o Example.so
    • cc -dynamiclib Example.c -o Example.dylib

How does it work?

  • Since Cryptol is interpreted, number/types of arguments to foreign functions are not known statically
  • libffi calls functions with the right calling conventions given interface at runtime
    • Rant about libffi in Haskell

Supported types

Basic types

Cryptol C
Bit uint8_t
[K]Bit, 0 <= K <= 8 uint8_t
[K]Bit, 8 < K <= 16 uint16_t
[K]Bit, 16 < K <= 32 uint32_t
[K]Bit, 32 < K <= 64 uint64_t
Float32 float
Float64 double

Supported types

Multiple arguments?

Cryptol C
A -> B -> ... -> Z Z' (A', B', ...)

Supported types

More complex types

  • So far it has been pretty simple
  • How about sequences? I guess we need arrays
    • Who allocates the memory?
    • Who owns the memory?
    • How are they passed vs returned?

Supported types

Sequences

Cryptol C
[n1][n2]...[nk]T U*

where

T U
[K]Bit, 0 <= K <= 8 uint8_t
[K]Bit, 8 < K <= 16 uint16_t
[K]Bit, 16 < K <= 32 uint32_t
[K]Bit, 32 < K <= 64 uint64_t
Float32 float
Float64 double
  • Cryptol allocates and owns all memory
  • Sequence as Cryptol parameter: const U* C parameter
  • Sequence as Cryptol return type: U* C output parameter
    • Passed after Cryptol parameters
    • Return type of C function is void
  • Multidimensional sequences: flatten (except the [K]Bit part)
  • How about n1, n2, ..., nk?
    • Must be fin

Supported types kinds?

Numeric type arguments

Cryptol C
# size_t
  • Must be fin
  • Type arguments are passed before any value arguments

Supported types

Tuples and records as parameters

  • Passed as separate arguments
Cryptol C (multiple!)
(T1, T2, ..., Tn) U1, U2, ..., Un
{s1: T1, s2: T2, ..., sn: Tn} U1, U2, ..., Un

where

Cryptol C
Ti Ui

Note:

  • Order of record fields is however it's written in the type in the source code
  • Uncurried and curried Cryptol functions correspond to the same C function
  • () corresponds to no arguments at all
  • Can be nested

Supported types

Tuples and records as return types

  • Returned as separate output parameters
  • This means the component types need to become pointers
    • Except for sequences which are already pointers
Cryptol C (multiple!)
(T1, T2, ..., Tn) V1, V2, ..., Vn
{s1: T1, s2: T2, ..., sn: Tn} V1, V2, ..., Vn

where

Cryptol parameter C
Ti Ui

and Vi = if Ui is a pointer type then Ui else Ui*

Note:

  • Return type of C function is void
  • () corresponds to no return value and no output arguments
  • Can be nested

Supported types

Type synonyms

  • Type synonyms are expanded before applying the previous rules

Examples

foreign f : {n} (fin n) => [n][10] -> {a : Bit, b : [64]} -> (Float64, [n + 1][20])
void f(size_t n, uint16_t *in0, uint8_t in1, uint64_t in2,
       double *out0, uint32_t *out1);
  • More examples in FFI tests

Evaluation

  • All arguments are fully evaluated
  • Result will be fully evaluated too (of course)

Questionable uses of FFI

foreign print : {n} (fin n) => [n][8] -> ()
void print(size_t n, char *s) {
  printf("%.*s\n", (int) n, s);
}
Main> print "Hello world!"
Hello world!
()
foreign launchMissiles : ()

etc

  • Cryptol functions should be pure
  • Beware of laziness

AES example

  • Replacement for SuiteB module using AES-NI for AES functions
  • ~5.5x faster
    • But could actually be much faster, if not limited to 1 block

Somewhat interesting(?) implementation details

  • ForeignPtr for dlclose
  • Interesting types in FFI marshalling
getMarshalBasicArg :: FFIBasicType ->
    (forall a. FFIArg a => (GenValue Concrete -> Eval a) -> b) -> b
data GetRet = GetRet
    { getRetAsValue   :: forall a. FFIRet a => IO a
    , getRetAsOutArgs :: [SomeFFIArg] -> IO () }

Future work

  • Automatically generate header files from foreign declarations
  • Support Windows
  • Support Integer/Z
  • Cryptol implementation of foreign functions + SAW integration

Other stuff (not FFI)

  • :time command for benchmarking evaluation of Cryptol expressions
    • Evaluates it a bunch of times and reports the average time
  • Import specific operators
    • import M ((<+>))
  • Fixed fixity (fixedity?) of prefix operators
    • -x**2 parses as -(x**2) not (-x)**2

Thank you!

  • Iavor, Eddy, Ryan

Questions?

  • Why is the Cryptol logo a water drop