{"id":35098649,"url":"https://github.com/go-webgpu/goffi","last_synced_at":"2026-03-03T21:08:07.582Z","repository":{"id":309956661,"uuid":"1038127258","full_name":"go-webgpu/goffi","owner":"go-webgpu","description":"Pure Go FFI for WebGPU - CGO-free GPU access via wgpu-native bindings","archived":false,"fork":false,"pushed_at":"2026-02-27T09:35:31.000Z","size":279,"stargazers_count":20,"open_issues_count":2,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-27T14:58:46.517Z","etag":null,"topics":["cgo-free","ffi","go","golang","golang-library","gpu","graphics","rendering","webgpu","wgpu"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/go-webgpu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-14T17:01:45.000Z","updated_at":"2026-02-27T09:34:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"3560870f-ed7c-46d2-b35b-3fd159ca7ee2","html_url":"https://github.com/go-webgpu/goffi","commit_stats":null,"previous_names":["go-webgpu/goffi"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/go-webgpu/goffi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-webgpu%2Fgoffi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-webgpu%2Fgoffi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-webgpu%2Fgoffi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-webgpu%2Fgoffi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/go-webgpu","download_url":"https://codeload.github.com/go-webgpu/goffi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-webgpu%2Fgoffi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30060988,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T18:21:05.932Z","status":"ssl_error","status_checked_at":"2026-03-03T18:20:59.341Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cgo-free","ffi","go","golang","golang-library","gpu","graphics","rendering","webgpu","wgpu"],"created_at":"2025-12-27T16:10:15.877Z","updated_at":"2026-03-03T21:08:07.567Z","avatar_url":"https://github.com/go-webgpu.png","language":"Go","readme":"# goffi — Zero-CGO FFI for Go\n\n[![CI](https://github.com/go-webgpu/goffi/actions/workflows/ci.yml/badge.svg)](https://github.com/go-webgpu/goffi/actions)\n[![codecov](https://codecov.io/gh/go-webgpu/goffi/graph/badge.svg)](https://codecov.io/gh/go-webgpu/goffi)\n[![Go Report Card](https://goreportcard.com/badge/github.com/go-webgpu/goffi)](https://goreportcard.com/report/github.com/go-webgpu/goffi)\n[![GitHub release](https://img.shields.io/github/v/release/go-webgpu/goffi)](https://github.com/go-webgpu/goffi/releases)\n[![Go version](https://img.shields.io/github/go-mod-go-version/go-webgpu/goffi)](https://github.com/go-webgpu/goffi/blob/main/go.mod)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Go Reference](https://pkg.go.dev/badge/github.com/go-webgpu/goffi.svg)](https://pkg.go.dev/github.com/go-webgpu/goffi)\n[![Dev.to](https://img.shields.io/badge/dev.to-deep%20dive-0A0A0A?logo=devdotto)](https://dev.to/kolkov/goffi-zero-cgo-foreign-function-interface-for-go-how-we-call-c-libraries-without-a-c-compiler-ca5)\n\n**Pure Go Foreign Function Interface** for calling C libraries without CGO.\nDesigned for WebGPU and GPU computing — zero C dependencies, zero per-call allocations, 88–114 ns overhead.\n\n\u003e **Deep dive:** [How We Call C Libraries Without a C Compiler](https://dev.to/kolkov/goffi-zero-cgo-foreign-function-interface-for-go-how-we-call-c-libraries-without-a-c-compiler-ca5) — architecture, assembly, callbacks, and ecosystem.\n\n```go\n// Load library, prepare once, call many times — no CGO required\nhandle, _ := ffi.LoadLibrary(\"wgpu_native.dll\")\nsym, _ := ffi.GetSymbol(handle, \"wgpuCreateInstance\")\n\ncif := \u0026types.CallInterface{}\nffi.PrepareCallInterface(cif, types.DefaultCall, returnType, argTypes)\nffi.CallFunction(cif, sym, unsafe.Pointer(\u0026result), args)\n```\n\n---\n\n## Features\n\n| | Feature | Details |\n|---|---------|---------|\n| **Zero CGO** | Pure Go | No C compiler needed. `go get` and build. |\n| **Fast** | 88–114 ns/op | Pre-computed CIF, zero per-call allocations |\n| **Cross-platform** | 6 targets | Windows, Linux, macOS × AMD64 + ARM64 |\n| **Callbacks** | C→Go safe | `crosscall2` integration, works from any C thread |\n| **Type-safe** | Runtime validation | 5 typed error types with `errors.As()` support |\n| **Struct passing** | Full ABI | ≤8B (RAX), 9–16B (RAX+RDX), \u003e16B (sret) |\n| **Context** | Timeouts | `CallFunctionContext(ctx, ...)` cancellation |\n| **Tested** | 89% coverage | CI on Linux, Windows, macOS |\n\n---\n\n## Quick Start\n\n### Installation\n\n```bash\ngo get github.com/go-webgpu/goffi\n```\n\n### Requirements\n\ngoffi requires `CGO_ENABLED=0`. This is automatic when no C compiler is installed or when cross-compiling. If you have gcc/clang:\n\n```bash\nCGO_ENABLED=0 go build ./...\n```\n\n\u003e **Why?** goffi uses Go's `cgo_import_dynamic` for dynamic library loading, which only activates when CGO is disabled.\n\n### Example: Calling strlen\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/go-webgpu/goffi/ffi\"\n\t\"github.com/go-webgpu/goffi/types\"\n)\n\nfunc main() {\n\t// Load platform-specific C library\n\tlibName := \"libc.so.6\"\n\tif runtime.GOOS == \"windows\" {\n\t\tlibName = \"msvcrt.dll\"\n\t}\n\n\thandle, err := ffi.LoadLibrary(libName)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer ffi.FreeLibrary(handle)\n\n\tstrlen, err := ffi.GetSymbol(handle, \"strlen\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Prepare call interface once — reuse for all subsequent calls\n\tcif := \u0026types.CallInterface{}\n\terr = ffi.PrepareCallInterface(\n\t\tcif,\n\t\ttypes.DefaultCall,                                     // auto-detects platform ABI\n\t\ttypes.UInt64TypeDescriptor,                            // return: size_t\n\t\t[]*types.TypeDescriptor{types.PointerTypeDescriptor},  // arg: const char*\n\t)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Call strlen — avalue elements are pointers TO argument values\n\ttestStr := \"Hello, goffi!\\x00\"\n\tstrPtr := uintptr(unsafe.Pointer(unsafe.StringData(testStr)))\n\tvar length uint64\n\n\terr = ffi.CallFunction(cif, strlen, unsafe.Pointer(\u0026length), []unsafe.Pointer{unsafe.Pointer(\u0026strPtr)})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"strlen(%q) = %d\\n\", testStr[:len(testStr)-1], length)\n\t// Output: strlen(\"Hello, goffi!\") = 13\n}\n```\n\n---\n\n## Performance\n\n**FFI overhead: 88–114 ns/op** (Windows AMD64, Intel i7-1255U)\n\n| Benchmark | Time | Allocations |\n|-----------|------|-------------|\n| Empty function (`getpid`) | 88 ns | 2 allocs |\n| Integer argument (`abs`) | 114 ns | 3 allocs |\n| String processing (`strlen`) | 98 ns | 3 allocs |\n\nAt 60 FPS with ~50 FFI calls per frame, overhead is **5 µs per frame** — 0.03% of the 16.6 ms budget. Unmeasurable in profiling.\n\nSee [docs/PERFORMANCE.md](docs/PERFORMANCE.md) for detailed analysis, optimization strategies, and when NOT to use goffi.\n\n---\n\n## Architecture\n\ngoffi transitions from Go's managed runtime to C code through three layers:\n\n```\nGo Code\n  │  ffi.CallFunction()\n  ▼\nruntime.cgocall               ← Go runtime: system stack switch, GC coordination\n  │\n  ▼\nAssembly Wrapper              ← Hand-written: load GP/SSE registers per ABI\n  │  CALL target_function\n  ▼\nC Function                    ← External library\n```\n\n**Three ABIs, hand-written assembly for each:**\n\n| ABI | GP Registers | FP Registers | Notes |\n|-----|-------------|-------------|-------|\n| System V AMD64 | RDI, RSI, RDX, RCX, R8, R9 | XMM0–XMM7 | Linux, macOS, FreeBSD |\n| Win64 | RCX, RDX, R8, R9 | XMM0–XMM3 | 32-byte shadow space mandatory |\n| AAPCS64 | X0–X7 | D0–D7 | HFA support for ARM64 |\n\nSee [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full technical deep dive.\n\n---\n\n## Callbacks (C → Go)\n\nWebGPU fires async callbacks from internal Metal/Vulkan threads. These threads have no goroutine — calling Go directly would crash.\n\ngoffi uses `crosscall2` for safe C→Go transitions from any thread:\n\n```go\ncb := ffi.NewCallback(func(status uint32, adapter uintptr, msg uintptr, ud uintptr) {\n    // Safe even when called from a C thread\n    result.handle = adapter\n    close(done)\n})\n\nffi.CallFunction(cif, wgpuRequestAdapter, nil, args)\n\u003c-done // Wait for GPU driver callback\n```\n\n2000 pre-compiled trampoline entries per process. AMD64: 5 bytes/entry. ARM64: 8 bytes/entry.\n\n---\n\n## Error Handling\n\nFive typed error types for precise diagnostics:\n\n```go\nhandle, err := ffi.LoadLibrary(\"nonexistent.dll\")\nif err != nil {\n\tvar libErr *ffi.LibraryError\n\tif errors.As(err, \u0026libErr) {\n\t\tfmt.Printf(\"Failed to %s %q: %v\\n\", libErr.Operation, libErr.Name, libErr.Err)\n\t}\n}\n```\n\n| Error Type | When |\n|------------|------|\n| `InvalidCallInterfaceError` | CIF preparation failures |\n| `LibraryError` | Library loading / symbol lookup |\n| `CallingConventionError` | Unsupported calling convention |\n| `TypeValidationError` | Invalid type descriptor |\n| `UnsupportedPlatformError` | Platform not supported |\n\n---\n\n## Comparison: goffi vs purego vs CGO\n\n| Feature | **goffi** | purego | CGO |\n|---------|-----------|--------|-----|\n| C compiler required | No | No | Yes |\n| API style | libffi-like (prepare once, call many) | reflect-based (RegisterFunc) | Native |\n| Per-call allocations | Zero (CIF reusable) | reflect + sync.Pool per call | Zero |\n| Struct pass/return | Full (RAX+RDX, sret) | Partial (no Windows structs) | Full |\n| Callback float returns | XMM0 in asm | Not supported (panic) | Full |\n| ARM64 HFA detection | Recursive (nested structs) | Partial (bug in nested path) | Full |\n| Typed errors | 5 types + errors.As() | Generic | N/A |\n| Context support | Timeouts/cancellation | No | No |\n| C-thread callbacks | crosscall2 | crosscall2 | Full |\n| String/bool/slice args | Raw pointers only | Auto-marshaling | Full |\n| Platform breadth | 6 targets | 8 GOARCH / 20+ OS×ARCH | All |\n| AMD64 overhead | 88–114 ns | Not published | ~140 ns (Go 1.26 claims ~30% reduction) |\n\n**Choose goffi** for GPU/real-time workloads: struct passing, zero per-call overhead, callback float returns, typed errors.\n\n**Choose purego** for general-purpose bindings: string auto-marshaling, broad architecture support, less boilerplate.\n\n**See also:** [JupiterRider/ffi](https://github.com/JupiterRider/ffi) — pure Go binding for libffi via purego. Supports struct pass/return and variadic functions; requires libffi at runtime.\n\n---\n\n## Known Limitations\n\n**Windows: C++ exceptions may crash the program** ([#12516](https://github.com/golang/go/issues/12516))\n- Go runtime limitation, not goffi-specific. Go 1.22+ added partial SEH support ([#58542](https://github.com/golang/go/issues/58542)), but edge cases remain.\n- Workaround: build native libraries with `panic=abort`.\n\n**Windows: float return values not captured from XMM0**\n- `syscall.SyscallN` returns RAX only. Go `syscall` package limitation.\n\n**Variadic functions not supported** (`printf`, `sprintf`)\n- Use non-variadic wrappers. Planned for v0.5.0.\n\n**Struct packing follows System V ABI only**\n- Windows `#pragma pack` not honored. Manually specify `Size`/`Alignment` in `TypeDescriptor`.\n\n**No bitfields** in struct types.\n\n**Unix: duplicate symbol conflict with purego** ([#22](https://github.com/go-webgpu/goffi/issues/22))\n- When using goffi and purego in the same binary with `CGO_ENABLED=0`, the linker reports `duplicated definition of symbol _cgo_init`. Both libraries include `internal/fakecgo` which defines identical runtime symbols.\n- Workaround: build with `-tags nofakecgo` to disable goffi's fakecgo, relying on purego's copy:\n  ```bash\n  CGO_ENABLED=0 go build -tags nofakecgo ./...\n  ```\n\n---\n\n## Platform Support\n\n| Platform | Arch | ABI | Since | CI |\n|----------|------|-----|-------|----|\n| Windows | amd64 | Win64 | v0.1.0 | Tested |\n| Linux | amd64 | System V | v0.1.0 | Tested |\n| macOS | amd64 | System V | v0.1.1 | Tested |\n| FreeBSD | amd64 | System V | v0.1.0 | Untested |\n| Linux | arm64 | AAPCS64 | v0.3.0 | Cross-compile verified |\n| macOS | arm64 | AAPCS64 | v0.3.7 | Tested (M3 Pro) |\n\n---\n\n## Roadmap\n\n| Version | Status | Highlights |\n|---------|--------|------------|\n| v0.2.0 | Released | Callback API, 2000-entry trampoline table |\n| v0.3.x | Released | ARM64 (AAPCS64), HFA, Apple Silicon |\n| v0.4.0 | Released | crosscall2 for C-thread callbacks |\n| v0.4.1 | Released | ABI compliance audit — 10/11 gaps fixed |\n| **v0.5.0** | **Next** | Variadic functions, builder API, Windows struct packing |\n| v1.0.0 | Planned | API stability (SemVer 2.0), security audit |\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history and [ROADMAP.md](ROADMAP.md) for the full plan.\n\n---\n\n## Testing\n\n```bash\ngo test ./...                          # all tests\ngo test -cover ./...                   # with coverage (89%)\ngo test -bench=. -benchmem ./ffi       # benchmarks\ngo test -v ./ffi                       # verbose, auto-detects platform\n```\n\n---\n\n## Documentation\n\n| Document | Description |\n|----------|-------------|\n| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Technical architecture: assembly, ABIs, callbacks |\n| [docs/PERFORMANCE.md](docs/PERFORMANCE.md) | Benchmarks, optimization strategies, Go 1.26 |\n| [CHANGELOG.md](CHANGELOG.md) | Version history, migration guides |\n| [ROADMAP.md](ROADMAP.md) | Development roadmap to v1.0 |\n| [CONTRIBUTING.md](CONTRIBUTING.md) | Contribution guidelines |\n| [SECURITY.md](SECURITY.md) | Security policy |\n| [examples/](examples/) | Working code examples |\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n1. Fork → feature branch → tests (80%+ coverage) → lint → PR\n2. Conventional commits: `feat:`, `fix:`, `docs:`, `test:`\n\n---\n\n## Acknowledgments\n\n- **[purego](https://github.com/ebitengine/purego)** — proved that pure Go FFI is possible. The `crosscall2` callback mechanism, `fakecgo` approach, and assembly trampoline patterns were pioneered by purego. goffi exists because purego cleared the path.\n- **[libffi](https://sourceware.org/libffi/)** — reference for FFI architecture patterns and CIF design.\n- **Go runtime** — `runtime.cgocall` for GC-safe stack switching, `crosscall2` for C→Go transitions.\n\n---\n\n## Ecosystem\n\ngoffi powers an ecosystem of pure Go GPU libraries:\n\n| Project | Description |\n|---------|-------------|\n| [go-webgpu/webgpu](https://github.com/go-webgpu/webgpu) | Zero-CGO WebGPU bindings (wgpu-native) |\n| [born-ml/born](https://github.com/born-ml/born) | ML framework for Go, GPU-accelerated |\n| [gogpu](https://github.com/gogpu) | GPU computing platform — dual Rust + Pure Go backends |\n| [wgpu-native](https://github.com/gfx-rs/wgpu-native) | Native WebGPU implementation (upstream) |\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\n*goffi v0.4.1 | [GitHub](https://github.com/go-webgpu/goffi) | [pkg.go.dev](https://pkg.go.dev/github.com/go-webgpu/goffi) | [Dev.to](https://dev.to/kolkov/goffi-zero-cgo-foreign-function-interface-for-go-how-we-call-c-libraries-without-a-c-compiler-ca5)*\n","funding_links":[],"categories":["Miscellaneous","Microsoft Office"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-webgpu%2Fgoffi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgo-webgpu%2Fgoffi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-webgpu%2Fgoffi/lists"}