|
| 1 | +{{~ Aditionally delete this line and fill out the template below ~}} |
| 2 | + |
| 3 | +# {{PROJECT}} ABI/FFI Documentation |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This library follows the **Hyperpolymath RSR Standard** for ABI and FFI design: |
| 8 | + |
| 9 | +- **ABI (Application Binary Interface)** defined in **Idris2** with formal proofs |
| 10 | +- **FFI (Foreign Function Interface)** implemented in **Zig** for C compatibility |
| 11 | +- **Generated C headers** bridge Idris2 ABI to Zig FFI |
| 12 | +- **Any language** can call through standard C ABI |
| 13 | + |
| 14 | +## Architecture |
| 15 | + |
| 16 | +``` |
| 17 | +┌─────────────────────────────────────────────┐ |
| 18 | +│ ABI Definitions (Idris2) │ |
| 19 | +│ src/abi/ │ |
| 20 | +│ - Types.idr (Type definitions) │ |
| 21 | +│ - Layout.idr (Memory layout proofs) │ |
| 22 | +│ - Foreign.idr (FFI declarations) │ |
| 23 | +└─────────────────┬───────────────────────────┘ |
| 24 | + │ |
| 25 | + │ generates (at compile time) |
| 26 | + ▼ |
| 27 | +┌─────────────────────────────────────────────┐ |
| 28 | +│ C Headers (auto-generated) │ |
| 29 | +│ generated/abi/{{project}}.h │ |
| 30 | +└─────────────────┬───────────────────────────┘ |
| 31 | + │ |
| 32 | + │ imported by |
| 33 | + ▼ |
| 34 | +┌─────────────────────────────────────────────┐ |
| 35 | +│ FFI Implementation (Zig) │ |
| 36 | +│ ffi/zig/src/main.zig │ |
| 37 | +│ - Implements C-compatible functions │ |
| 38 | +│ - Zero-cost abstractions │ |
| 39 | +│ - Memory-safe by default │ |
| 40 | +└─────────────────┬───────────────────────────┘ |
| 41 | + │ |
| 42 | + │ compiled to lib{{project}}.so/.a |
| 43 | + ▼ |
| 44 | +┌─────────────────────────────────────────────┐ |
| 45 | +│ Any Language via C ABI │ |
| 46 | +│ - Rust, ReScript, Julia, Python, etc. │ |
| 47 | +└─────────────────────────────────────────────┘ |
| 48 | +``` |
| 49 | + |
| 50 | +## Directory Structure |
| 51 | + |
| 52 | +``` |
| 53 | +{{project}}/ |
| 54 | +├── src/ |
| 55 | +│ ├── abi/ # ABI definitions (Idris2) |
| 56 | +│ │ ├── Types.idr # Core type definitions with proofs |
| 57 | +│ │ ├── Layout.idr # Memory layout verification |
| 58 | +│ │ └── Foreign.idr # FFI function declarations |
| 59 | +│ └── lib/ # Core library (any language) |
| 60 | +│ |
| 61 | +├── ffi/ |
| 62 | +│ └── zig/ # FFI implementation (Zig) |
| 63 | +│ ├── build.zig # Build configuration |
| 64 | +│ ├── build.zig.zon # Dependencies |
| 65 | +│ ├── src/ |
| 66 | +│ │ └── main.zig # C-compatible FFI implementation |
| 67 | +│ ├── test/ |
| 68 | +│ │ └── integration_test.zig |
| 69 | +│ └── include/ |
| 70 | +│ └── {{project}}.h # C header (optional, can be generated) |
| 71 | +│ |
| 72 | +├── generated/ # Auto-generated files |
| 73 | +│ └── abi/ |
| 74 | +│ └── {{project}}.h # Generated from Idris2 ABI |
| 75 | +│ |
| 76 | +└── bindings/ # Language-specific wrappers (optional) |
| 77 | + ├── rust/ |
| 78 | + ├── rescript/ |
| 79 | + └── julia/ |
| 80 | +``` |
| 81 | + |
| 82 | +## Why Idris2 for ABI? |
| 83 | + |
| 84 | +### 1. **Formal Verification** |
| 85 | + |
| 86 | +Idris2's dependent types allow proving properties about the ABI at compile-time: |
| 87 | + |
| 88 | +```idris |
| 89 | +-- Prove struct size is correct |
| 90 | +public export |
| 91 | +exampleStructSize : HasSize ExampleStruct 16 |
| 92 | +
|
| 93 | +-- Prove field alignment is correct |
| 94 | +public export |
| 95 | +fieldAligned : Divides 8 (offsetOf ExampleStruct.field) |
| 96 | +
|
| 97 | +-- Prove ABI is platform-compatible |
| 98 | +public export |
| 99 | +abiCompatible : Compatible (ABI 1) (ABI 2) |
| 100 | +``` |
| 101 | + |
| 102 | +### 2. **Type Safety** |
| 103 | + |
| 104 | +Encode invariants that C/Zig cannot express: |
| 105 | + |
| 106 | +```idris |
| 107 | +-- Non-null pointer guaranteed at type level |
| 108 | +data Handle : Type where |
| 109 | + MkHandle : (ptr : Bits64) -> {auto 0 nonNull : So (ptr /= 0)} -> Handle |
| 110 | +
|
| 111 | +-- Array with length proof |
| 112 | +data Buffer : (n : Nat) -> Type where |
| 113 | + MkBuffer : Vect n Byte -> Buffer n |
| 114 | +``` |
| 115 | + |
| 116 | +### 3. **Platform Abstraction** |
| 117 | + |
| 118 | +Platform-specific types with compile-time selection: |
| 119 | + |
| 120 | +```idris |
| 121 | +CInt : Platform -> Type |
| 122 | +CInt Linux = Bits32 |
| 123 | +CInt Windows = Bits32 |
| 124 | +
|
| 125 | +CSize : Platform -> Type |
| 126 | +CSize Linux = Bits64 |
| 127 | +CSize Windows = Bits64 |
| 128 | +``` |
| 129 | + |
| 130 | +### 4. **Safe Evolution** |
| 131 | + |
| 132 | +Prove that new ABI versions are backward-compatible: |
| 133 | + |
| 134 | +```idris |
| 135 | +-- Compiler enforces compatibility |
| 136 | +abiUpgrade : ABI 1 -> ABI 2 |
| 137 | +abiUpgrade old = MkABI2 { |
| 138 | + -- Must preserve all v1 fields |
| 139 | + v1_compat = old, |
| 140 | + -- Can add new fields |
| 141 | + new_features = defaults |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +## Why Zig for FFI? |
| 146 | + |
| 147 | +### 1. **C ABI Compatibility** |
| 148 | + |
| 149 | +Zig exports C-compatible functions naturally: |
| 150 | + |
| 151 | +```zig |
| 152 | +export fn library_function(param: i32) i32 { |
| 153 | + return param * 2; |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +### 2. **Memory Safety** |
| 158 | + |
| 159 | +Compile-time safety without runtime overhead: |
| 160 | + |
| 161 | +```zig |
| 162 | +// Null check enforced at compile time |
| 163 | +const handle = init() orelse return error.InitFailed; |
| 164 | +defer free(handle); |
| 165 | +``` |
| 166 | + |
| 167 | +### 3. **Cross-Compilation** |
| 168 | + |
| 169 | +Built-in cross-compilation to any platform: |
| 170 | + |
| 171 | +```bash |
| 172 | +zig build -Dtarget=x86_64-linux |
| 173 | +zig build -Dtarget=aarch64-macos |
| 174 | +zig build -Dtarget=x86_64-windows |
| 175 | +``` |
| 176 | + |
| 177 | +### 4. **Zero Dependencies** |
| 178 | + |
| 179 | +No runtime, no libc required (unless explicitly needed): |
| 180 | + |
| 181 | +```zig |
| 182 | +// Minimal binary size |
| 183 | +pub const lib = @import("std"); |
| 184 | +// Only includes what you use |
| 185 | +``` |
| 186 | + |
| 187 | +## Building |
| 188 | + |
| 189 | +### Build FFI Library |
| 190 | + |
| 191 | +```bash |
| 192 | +cd ffi/zig |
| 193 | +zig build # Build debug |
| 194 | +zig build -Doptimize=ReleaseFast # Build optimized |
| 195 | +zig build test # Run tests |
| 196 | +``` |
| 197 | + |
| 198 | +### Generate C Header from Idris2 ABI |
| 199 | + |
| 200 | +```bash |
| 201 | +cd src/abi |
| 202 | +idris2 --cg c-header Types.idr -o ../../generated/abi/{{project}}.h |
| 203 | +``` |
| 204 | + |
| 205 | +### Cross-Compile |
| 206 | + |
| 207 | +```bash |
| 208 | +cd ffi/zig |
| 209 | + |
| 210 | +# Linux x86_64 |
| 211 | +zig build -Dtarget=x86_64-linux |
| 212 | + |
| 213 | +# macOS ARM64 |
| 214 | +zig build -Dtarget=aarch64-macos |
| 215 | + |
| 216 | +# Windows x86_64 |
| 217 | +zig build -Dtarget=x86_64-windows |
| 218 | +``` |
| 219 | + |
| 220 | +## Usage |
| 221 | + |
| 222 | +### From C |
| 223 | + |
| 224 | +```c |
| 225 | +#include "{{project}}.h" |
| 226 | + |
| 227 | +int main() { |
| 228 | + void* handle = {{project}}_init(); |
| 229 | + if (!handle) return 1; |
| 230 | + |
| 231 | + int result = {{project}}_process(handle, 42); |
| 232 | + if (result != 0) { |
| 233 | + const char* err = {{project}}_last_error(); |
| 234 | + fprintf(stderr, "Error: %s\n", err); |
| 235 | + } |
| 236 | + |
| 237 | + {{project}}_free(handle); |
| 238 | + return 0; |
| 239 | +} |
| 240 | +``` |
| 241 | + |
| 242 | +Compile with: |
| 243 | +```bash |
| 244 | +gcc -o example example.c -l{{project}} -L./zig-out/lib |
| 245 | +``` |
| 246 | + |
| 247 | +### From Idris2 |
| 248 | + |
| 249 | +```idris |
| 250 | +import {{PROJECT}}.ABI.Foreign |
| 251 | +
|
| 252 | +main : IO () |
| 253 | +main = do |
| 254 | + Just handle <- init |
| 255 | + | Nothing => putStrLn "Failed to initialize" |
| 256 | +
|
| 257 | + Right result <- process handle 42 |
| 258 | + | Left err => putStrLn $ "Error: " ++ errorDescription err |
| 259 | +
|
| 260 | + free handle |
| 261 | + putStrLn "Success" |
| 262 | +``` |
| 263 | + |
| 264 | +### From Rust |
| 265 | + |
| 266 | +```rust |
| 267 | +#[link(name = "{{project}}")] |
| 268 | +extern "C" { |
| 269 | + fn {{project}}_init() -> *mut std::ffi::c_void; |
| 270 | + fn {{project}}_free(handle: *mut std::ffi::c_void); |
| 271 | + fn {{project}}_process(handle: *mut std::ffi::c_void, input: u32) -> i32; |
| 272 | +} |
| 273 | + |
| 274 | +fn main() { |
| 275 | + unsafe { |
| 276 | + let handle = {{project}}_init(); |
| 277 | + assert!(!handle.is_null()); |
| 278 | + |
| 279 | + let result = {{project}}_process(handle, 42); |
| 280 | + assert_eq!(result, 0); |
| 281 | + |
| 282 | + {{project}}_free(handle); |
| 283 | + } |
| 284 | +} |
| 285 | +``` |
| 286 | + |
| 287 | +### From Julia |
| 288 | + |
| 289 | +```julia |
| 290 | +const lib{{project}} = "lib{{project}}" |
| 291 | + |
| 292 | +function init() |
| 293 | + handle = ccall((:{{project}}_init, lib{{project}}), Ptr{Cvoid}, ()) |
| 294 | + handle == C_NULL && error("Failed to initialize") |
| 295 | + handle |
| 296 | +end |
| 297 | + |
| 298 | +function process(handle, input) |
| 299 | + result = ccall((:{{project}}_process, lib{{project}}), Cint, (Ptr{Cvoid}, UInt32), handle, input) |
| 300 | + result |
| 301 | +end |
| 302 | + |
| 303 | +function cleanup(handle) |
| 304 | + ccall((:{{project}}_free, lib{{project}}), Cvoid, (Ptr{Cvoid},), handle) |
| 305 | +end |
| 306 | + |
| 307 | +# Usage |
| 308 | +handle = init() |
| 309 | +try |
| 310 | + result = process(handle, 42) |
| 311 | + println("Result: $result") |
| 312 | +finally |
| 313 | + cleanup(handle) |
| 314 | +end |
| 315 | +``` |
| 316 | + |
| 317 | +## Testing |
| 318 | + |
| 319 | +### Unit Tests (Zig) |
| 320 | + |
| 321 | +```bash |
| 322 | +cd ffi/zig |
| 323 | +zig build test |
| 324 | +``` |
| 325 | + |
| 326 | +### Integration Tests |
| 327 | + |
| 328 | +```bash |
| 329 | +cd ffi/zig |
| 330 | +zig build test-integration |
| 331 | +``` |
| 332 | + |
| 333 | +### ABI Verification (Idris2) |
| 334 | + |
| 335 | +```idris |
| 336 | +-- Compile-time verification |
| 337 | +%runElab verifyABI |
| 338 | +
|
| 339 | +-- Runtime checks |
| 340 | +main : IO () |
| 341 | +main = do |
| 342 | + verifyLayoutsCorrect |
| 343 | + verifyAlignmentsCorrect |
| 344 | + putStrLn "ABI verification passed" |
| 345 | +``` |
| 346 | + |
| 347 | +## Contributing |
| 348 | + |
| 349 | +When modifying the ABI/FFI: |
| 350 | + |
| 351 | +1. **Update ABI first** (`src/abi/*.idr`) |
| 352 | + - Modify type definitions |
| 353 | + - Update proofs |
| 354 | + - Ensure backward compatibility |
| 355 | + |
| 356 | +2. **Generate C header** |
| 357 | + ```bash |
| 358 | + idris2 --cg c-header src/abi/Types.idr -o generated/abi/{{project}}.h |
| 359 | + ``` |
| 360 | + |
| 361 | +3. **Update FFI implementation** (`ffi/zig/src/main.zig`) |
| 362 | + - Implement new functions |
| 363 | + - Match ABI types exactly |
| 364 | + |
| 365 | +4. **Add tests** |
| 366 | + - Unit tests in Zig |
| 367 | + - Integration tests |
| 368 | + - ABI verification tests |
| 369 | + |
| 370 | +5. **Update documentation** |
| 371 | + - Function signatures |
| 372 | + - Usage examples |
| 373 | + - Migration guide (if breaking changes) |
| 374 | + |
| 375 | +## License |
| 376 | + |
| 377 | +{{LICENSE}} |
| 378 | + |
| 379 | +## See Also |
| 380 | + |
| 381 | +- [Idris2 Documentation](https://idris2.readthedocs.io) |
| 382 | +- [Zig Documentation](https://ziglang.org/documentation/master/) |
| 383 | +- [Rhodium Standard Repositories](https://github.com/hyperpolymath/rhodium-standard-repositories) |
| 384 | +- [FFI Migration Guide](../ffi-migration-guide.md) |
| 385 | +- [ABI Migration Guide](../abi-migration-guide.md) |
0 commit comments