|
2 | 2 |
|
3 | 3 | ## Introduction |
4 | 4 |
|
5 | | -TODO |
| 5 | +This chapter discusses how `hs-bindgen` generates bindings for C functions. |
6 | 6 |
|
7 | 7 | ## Safe vs unsafe foreign imports |
8 | 8 |
|
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. |
10 | 32 |
|
11 | 33 | ## Function addresses |
12 | 34 |
|
@@ -341,13 +363,79 @@ alloca $ \handlerPtr -> do |
341 | 363 |
|
342 | 364 | ## Userland CAPI |
343 | 365 |
|
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. |
347 | 400 |
|
348 | 401 | ### By-value `struct` arguments or return values |
349 | 402 |
|
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 | +``` |
351 | 439 |
|
352 | 440 | ### Static inline functions |
353 | 441 |
|
|
0 commit comments