Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Race detector compatibility** — replaced direct `uintptr→unsafe.Pointer` casts with double-indirection pattern (Go proposal [#58625](https://github.com/golang/go/issues/58625)) in callback dispatch and return handling. `CGO_ENABLED=1 go test -race` now passes cleanly
- **Classification: >16B structs** — `classifyArgumentAMD64` now correctly returns zero register usage for MEMORY class structs (previously claimed GP registers)
- **Classification: mixed eightbyte** — per-eightbyte SSE/INTEGER classification now walks all members with INTEGER-wins merge rule per System V ABI
- **Deprecated `reflect.Ptr`** — replaced with `reflect.Pointer` in callback validation (PR [#38](https://github.com/go-webgpu/goffi/pull/38), flagged by golangci-lint v2.12.1)

### Added
- `CGO_ENABLED=1` support ([#13](https://github.com/go-webgpu/goffi/issues/13), PR [#37](https://github.com/go-webgpu/goffi/pull/37) by [@jiyeyuran](https://github.com/jiyeyuran)) — goffi now builds and tests under both `CGO_ENABLED=0` (fakecgo) and `CGO_ENABLED=1` (real `runtime/cgo`). Enables race detector, coexistence with CGO libraries (gocv, database drivers, etc.), and resolves [#22](https://github.com/go-webgpu/goffi/issues/22) duplicate symbol conflict as alternative workaround
- C-thread callback test — `TestCallback_FromCThread` verifies `NewCallback` works when invoked from a `pthread_create`-spawned C thread under both CGO modes
- Unit tests for struct argument classification and value packing (25 test cases)
- **End-to-end struct argument tests** — `ffi/struct_e2e_test.go` compiles a C test library (`testdata/structtest.c`) via gcc at test time and validates 5 struct passing scenarios: ≤8B integer pair (issue #33 repro), ≤8B float pair (SSE), 16B two-eightbyte, >16B MEMORY class, and struct+scalar mixed arguments. Runs on Linux/macOS/FreeBSD where gcc is available; skipped gracefully elsewhere
- **End-to-end struct argument tests** — `ffi/struct_e2e_test.go` compiles a C test library (`testdata/structtest.c`) via gcc at test time and validates 5 struct passing scenarios: ≤8B integer pair (issue #33 repro), ≤8B float pair (SSE), 16B two-eightbyte, >16B MEMORY class, and struct+scalar mixed arguments. Runs on Linux/macOS/FreeBSD/Windows where gcc is available; skipped gracefully elsewhere
- **Callback struct argument support** ([#41](https://github.com/go-webgpu/goffi/issues/41), PR [#42](https://github.com/go-webgpu/goffi/pull/42) by [@pekim](https://github.com/pekim)) — callbacks (C→Go) now accept struct arguments on AMD64 Unix. All three size classes: ≤8B (INTEGER/SSE), 9-16B (two-eightbyte), >16B (MEMORY class from stack). Includes reflect-based ABI classification, nested struct support in `isStructAllFloats`, e2e tests with gcc-compiled C callback wrappers
- **End-to-end callback struct tests** — 5 callback scenarios with C functions that construct structs and pass them by value to Go callbacks

## [0.5.0] - 2026-03-29

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ffi.CallFunction(cif, sym, unsafe.Pointer(&result), args)
| **Zero CGO** | Pure Go | No C compiler needed. `go get` and build. |
| **Fast** | 88–114 ns/op | Pre-computed CIF, zero per-call allocations |
| **Cross-platform** | 7 targets | Windows, Linux, macOS, FreeBSD × AMD64 + ARM64 |
| **Callbacks** | C→Go safe | `crosscall2` integration, works from any C thread |
| **Callbacks** | C→Go safe | `crosscall2` integration, struct args, works from any C thread |
| **Type-safe** | Runtime validation | 5 typed error types with `errors.As()` support |
| **Struct pass/return** | Full ABI | Args: INTEGER/SSE classification. Returns: ≤8B (RAX), 9–16B (RAX+RDX), >16B (sret) |
| **Context** | Timeouts | `CallFunctionContext(ctx, ...)` cancellation |
Expand Down Expand Up @@ -224,6 +224,7 @@ if err != nil {
| API style | libffi-like (prepare once, call many) | reflect-based (RegisterFunc) | Native |
| Per-call allocations | Zero (CIF reusable) | reflect + sync.Pool per call | Zero |
| Struct pass/return | Full (RAX+RDX, sret) | Partial (no Windows structs) | Full |
| Callback struct args | ≤8B, 9-16B, >16B (AMD64) | Not supported (panic) | Full |
| Callback float returns | XMM0 in asm | Not supported (panic) | Full |
| ARM64 HFA detection | Recursive (nested structs) | Partial (bug in nested path) | Full |
| Typed errors | 5 types + errors.As() | Generic | N/A |
Expand Down
16 changes: 15 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,21 @@ Struct argument passing is verified by `ffi/struct_e2e_test.go`, which compiles
4. **24B triple** (`{int64, int64, int64}`) — MEMORY class, copied to stack
5. **Struct + scalar** — mixed register allocation

Tests run on Linux, macOS, and FreeBSD where gcc is available; skipped gracefully on Windows.
Tests run on Linux, macOS, FreeBSD, and Windows where gcc is available; skipped gracefully otherwise.

---

## Callback Struct Arguments (C→Go)

When C code calls a goffi callback with struct arguments, the callback dispatch (`callbackWrap` in `callback.go`) reconstructs struct values from CPU registers and stack using `reflect.Type`:

- **≤ 8 bytes**: single eightbyte in GP or XMM register. `isStructAllFloats()` determines classification (recursive, supports nested structs).
- **9-16 bytes**: two eightbytes, each classified independently via `classifyEightbyte()` using `reflect.StructField.Offset`.
- **\> 16 bytes**: MEMORY class — C caller copies bytes onto stack. Callback reads consecutive stack slots directly. No assembly changes needed.

Classification uses `reflect.Type` (not `types.TypeDescriptor`) since callback signatures are Go functions registered via `NewCallback()`.

**Limitations**: callback struct args supported on AMD64 Unix only. ARM64 and Windows callbacks do not yet support struct arguments.

---

Expand Down
Loading