Skip to content

Commit dbae38e

Browse files
author
Jonathan D.A. Jewell
committed
general update
1 parent 8266d5f commit dbae38e

File tree

16 files changed

+2227
-0
lines changed

16 files changed

+2227
-0
lines changed

ABI-FFI-README.md

Lines changed: 385 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,385 @@
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

Comments
 (0)