Skip to content

Commit b6d9041

Browse files
committed
Finish Function manual section
1 parent d45c10f commit b6d9041

File tree

1 file changed

+94
-6
lines changed

1 file changed

+94
-6
lines changed

manual/LowLevel/Functions.md

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,33 @@
22

33
## Introduction
44

5-
TODO
5+
This chapter discusses how `hs-bindgen` generates bindings for C functions.
66

77
## Safe vs unsafe foreign imports
88

9-
TODO
9+
When importing a C function, GHC allows us to choose between two calling
10+
conventions: `safe` and `unsafe`. The distinction is important:
11+
12+
* **Safe** foreign imports may call back into Haskell code. They are more
13+
expensive.
14+
* **Unsafe** foreign imports may not call back into Haskell. They are faster
15+
because they avoid this overhead, but using `unsafe` incorrectly can lead to
16+
undefined behavior.
17+
18+
Currently, `hs-bindgen` generates all function imports using the `safe` calling
19+
convention:
20+
21+
```hs
22+
foreign import ccall safe "B_function_name"
23+
function_name :: CInt -> IO CInt
24+
```
25+
26+
This is a conservative choice that ensures correctness in all cases. While it
27+
means we pay the cost of the safe calling convention even for functions that
28+
do not need it, this cost is typically negligible unless the function is called
29+
in a tight loop. Users who require the performance of `unsafe` imports can
30+
manually wrap the generated bindings with their own `unsafe` imports after
31+
verifying that the C function does not call back into Haskell.
1032

1133
## Function addresses
1234

@@ -341,13 +363,79 @@ alloca $ \handlerPtr -> do
341363
342364
## Userland CAPI
343365
344-
For certain C features, `hs-bindgen` generates C wrapper code to bridge between
345-
C and Haskell. These wrappers are necessary when the feature cannot be directly
346-
expressed using GHC's foreign function interface.
366+
GHC's foreign function interface has limitations on which C functions can be
367+
imported directly. For example, GHC cannot import functions that take or return
368+
structs by value. To work around these limitations, `hs-bindgen` generates C
369+
wrapper functions that can be imported by GHC, and then generates Haskell
370+
bindings to these wrappers instead of to the original C functions.
371+
372+
This approach is similar to GHC's `capi` calling convention, which also
373+
generates C wrappers to handle features that the FFI cannot express directly.
374+
However, by generating these wrappers ourselves at the userland level, we can
375+
extend the set of supported function signatures beyond what GHC's `capi`
376+
provides. For instance, we can handle by-value struct arguments and return
377+
values, which `capi` does not support.
378+
379+
The generated wrappers are named with a prefix based on the Haskell module
380+
name. For example, a C function `foo` might have a wrapper `B_foo` that we
381+
import instead:
382+
383+
```c
384+
// Generated C wrapper
385+
void B_print_point ( struct point * arg1 ) {
386+
print_point ( arg1 );
387+
}
388+
```
389+
390+
```hs
391+
-- Generated Haskell import
392+
foreign import ccall "B_print_point"
393+
print_point :: Ptr Point → IO ()
394+
```
395+
396+
This userland CAPI approach is used for all function imports in `hs-bindgen`,
397+
not just those with features GHC cannot handle directly. This provides a
398+
uniform interface and makes it straightforward to add support for additional
399+
C features in the future.
347400
348401
### By-value `struct` arguments or return values
349402
350-
TODO
403+
The GHC FFI does not support passing structs by value to or from C functions.
404+
For example, consider:
405+
406+
```c
407+
struct point byval ( struct point p );
408+
```
409+
410+
This function cannot be imported directly. Instead, `hs-bindgen` generates a C
411+
wrapper that accepts and return struct by pointer, performing the necessary
412+
conversions:
413+
414+
```c
415+
void B_byval ( struct point * arg
416+
, struct point * res ) {
417+
* res = byval (* arg );
418+
}
419+
```
420+
421+
This wrapper is then imported in Haskell:
422+
423+
```hs
424+
foreign import ccall safe "B_byval"
425+
byval_wrapper :: Ptr Point Ptr Point IO ()
426+
```
427+
428+
Finally, we generate a Haskell wrapper function that recovers the original
429+
by-value semantics using `with`, `alloca`, and `peek`:
430+
431+
```hs
432+
byval :: Point IO Point
433+
byval p =
434+
with p $ \ arg
435+
alloca $ \ res do
436+
B. byval_wrapper arg res
437+
peek res
438+
```
351439
352440
### Static inline functions
353441

0 commit comments

Comments
 (0)