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
62 changes: 60 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,63 @@ jobs:
fi
echo "All files are properly formatted ✓"

# Cross-compilation - Verify all 7 supported platforms compile
cross-compile:
name: Cross-Compile
runs-on: ubuntu-latest
needs: [lint, formatting]
env:
CGO_ENABLED: 0
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
cache: true

- name: Cross-compile all platforms
run: |
targets=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
"windows/amd64"
"windows/arm64"
)
FAILED=0
for target in "${targets[@]}"; do
os="${target%%/*}"
arch="${target##*/}"
echo "Building ${os}/${arch}..."
if GOOS=$os GOARCH=$arch go build ./... 2>&1; then
echo " ✅ ${os}/${arch}"
else
echo " ❌ ${os}/${arch} FAILED"
FAILED=1
fi
done

# FreeBSD requires -gcflags="-std" for fakecgo's //go:cgo_export_dynamic
echo "Building freebsd/amd64 (with -gcflags for fakecgo)..."
if GOOS=freebsd GOARCH=amd64 go build \
-gcflags="github.com/go-webgpu/goffi/internal/fakecgo=-std" \
./... 2>&1; then
echo " ✅ freebsd/amd64"
else
echo " ❌ freebsd/amd64 FAILED"
FAILED=1
fi

if [ $FAILED -eq 1 ]; then
echo "❌ Cross-compilation check FAILED"
exit 1
fi
echo "✅ All 7 platforms compile successfully"

# Unit tests - Platform-specific (Linux + Windows + macOS AMD64)
test:
name: Test - ${{ matrix.os }}
Expand Down Expand Up @@ -277,7 +334,7 @@ jobs:
# Final status - All checks passed
ci-success:
name: CI Success
needs: [lint, formatting, test, benchmarks, quality-gate]
needs: [lint, formatting, cross-compile, test, benchmarks, quality-gate]
runs-on: ubuntu-latest
if: success()
steps:
Expand All @@ -286,12 +343,13 @@ jobs:
echo "✅ All CI checks passed!"
echo "✅ Lint: PASSED"
echo "✅ Formatting: PASSED"
echo "✅ Cross-Compile: PASSED (7 platforms)"
echo "✅ Tests: PASSED"
echo " - Linux AMD64 (ubuntu-latest)"
echo " - Windows AMD64 (windows-latest)"
echo " - macOS ARM64 (macos-latest)"
echo "✅ Benchmarks: PASSED"
echo "✅ Quality Gate: PASSED"
echo ""
echo "Note: macOS Intel uses same ABI as Linux AMD64"
echo "Note: Windows ARM64 cross-compile verified, runtime tested by community"
echo "🚀 Ready for merge!"
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.5.0] - 2026-03-29

### Added
- **Windows ARM64 (Snapdragon X) support** — extended AAPCS64 ARM64 implementation to Windows via build tag changes. Uses `runtime.cgocall` (free on Windows without fakecgo). Tested on Samsung Galaxy Book 4 Edge with Snapdragon X Elite ([#31](https://github.com/go-webgpu/goffi/issues/31))
- **FreeBSD amd64 support** — added `internal/dl/dl_freebsd.go` and `dl_freebsd_nocgo.go` for `libc.so.7` dynamic loading. FreeBSD uses identical System V ABI as Linux. Requires `-gcflags="github.com/go-webgpu/goffi/internal/fakecgo=-std"` for `CGO_ENABLED=0` builds
- **CI cross-compilation check** — new job validates all 7 supported platforms compile correctly (linux/darwin/windows × amd64 + arm64 + freebsd/amd64)

### Changed
- Renamed ARM64 files to drop misleading "unix" suffix: `call_unix.go` → `call_arm64.go`, `syscall_unix_arm64.go/s` → `syscall_arm64.go/s`
- Extended `ffi/dl_windows.go` and `ffi/callback_windows.go` from `windows && amd64` to `windows` (all architectures)
- Extended 15+ build tags to include `freebsd` alongside `linux || darwin`

## [0.4.2] - 2026-03-03

### Fixed
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,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** | 6 targets | Windows, Linux, macOS × AMD64 + ARM64 |
| **Cross-platform** | 7 targets | Windows, Linux, macOS, FreeBSD × AMD64 + ARM64 |
| **Callbacks** | C→Go safe | `crosscall2` integration, works from any C thread |
| **Type-safe** | Runtime validation | 5 typed error types with `errors.As()` support |
| **Struct passing** | Full ABI | ≤8B (RAX), 9–16B (RAX+RDX), >16B (sret) |
Expand Down Expand Up @@ -224,7 +224,7 @@ if err != nil {
| Context support | Timeouts/cancellation | No | No |
| C-thread callbacks | crosscall2 | crosscall2 | Full |
| String/bool/slice args | Raw pointers only | Auto-marshaling | Full |
| Platform breadth | 6 targets | 8 GOARCH / 20+ OS×ARCH | All |
| Platform breadth | 7 targets | 8 GOARCH / 20+ OS×ARCH | All |
| AMD64 overhead | 88–114 ns | Not published | ~140 ns (Go 1.26 claims ~30% reduction) |

**Choose goffi** for GPU/real-time workloads: struct passing, zero per-call overhead, callback float returns, typed errors.
Expand Down Expand Up @@ -266,11 +266,12 @@ if err != nil {
| Platform | Arch | ABI | Since | CI |
|----------|------|-----|-------|----|
| Windows | amd64 | Win64 | v0.1.0 | Tested |
| Windows | arm64 | AAPCS64 | v0.5.0 | Tested (Snapdragon X) |
| Linux | amd64 | System V | v0.1.0 | Tested |
| macOS | amd64 | System V | v0.1.1 | Tested |
| FreeBSD | amd64 | System V | v0.1.0 | Untested |
| Linux | arm64 | AAPCS64 | v0.3.0 | Cross-compile verified |
| macOS | amd64 | System V | v0.1.1 | Tested |
| macOS | arm64 | AAPCS64 | v0.3.7 | Tested (M3 Pro) |
| FreeBSD | amd64 | System V | v0.5.0 | Cross-compile verified |

---

Expand All @@ -282,7 +283,8 @@ if err != nil {
| v0.3.x | Released | ARM64 (AAPCS64), HFA, Apple Silicon |
| v0.4.0 | Released | crosscall2 for C-thread callbacks |
| v0.4.1 | Released | ABI compliance audit — 10/11 gaps fixed |
| **v0.5.0** | **Next** | Variadic functions, builder API, Windows struct packing |
| v0.4.2 | Released | purego compatibility (`-tags nofakecgo`) |
| **v0.5.0** | **Next** | Windows ARM64, FreeBSD, variadic functions, builder API |
| v1.0.0 | Planned | API stability (SemVer 2.0), security audit |

See [CHANGELOG.md](CHANGELOG.md) for version history and [ROADMAP.md](ROADMAP.md) for the full plan.
Expand Down
5 changes: 3 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,11 @@ Five typed error types for precise error handling: `InvalidCallInterfaceError`,
|----------|-------------|-----|--------|
| **Linux** | AMD64 | System V | Production |
| **Windows** | AMD64 | Win64 | Production |
| **Windows** | ARM64 | AAPCS64 | Production (tested on Snapdragon X) |
| **macOS** | AMD64 | System V | Production |
| **FreeBSD** | AMD64 | System V | Production (untested) |
| **Linux** | ARM64 | AAPCS64 | Production |
| **macOS** | ARM64 | AAPCS64 | Production (tested on M3 Pro) |
| **FreeBSD** | AMD64 | System V | Cross-compile verified |
| **Linux** | ARM64 | AAPCS64 | Production |

---

Expand Down
2 changes: 1 addition & 1 deletion ffi/callback.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && amd64
//go:build (linux || darwin || freebsd) && amd64

// Package ffi provides callback support for Foreign Function Interface (Unix version).
// This file implements Go function registration as C callbacks using
Expand Down
2 changes: 1 addition & 1 deletion ffi/callback_arm64.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && arm64
//go:build (linux || darwin || freebsd) && arm64

// Package ffi provides callback support for Foreign Function Interface (ARM64 Unix version).
// This file implements Go function registration as C callbacks using
Expand Down
2 changes: 1 addition & 1 deletion ffi/callback_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && (amd64 || arm64)
//go:build (linux || darwin || freebsd) && (amd64 || arm64)

package ffi

Expand Down
2 changes: 1 addition & 1 deletion ffi/callback_windows.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build windows && amd64
//go:build windows

// Package ffi provides Foreign Function Interface capabilities.
// This file contains Windows-specific callback implementation using syscall.NewCallback.
Expand Down
2 changes: 1 addition & 1 deletion ffi/cgo_unsupported.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && cgo
//go:build (linux || darwin || freebsd) && cgo

// Package ffi cannot be built with CGO_ENABLED=1.
//
Expand Down
4 changes: 2 additions & 2 deletions ffi/dl_unix.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//go:build linux && (amd64 || arm64) && !cgo
//go:build (linux || freebsd) && (amd64 || arm64) && !cgo

// Linux library loading - OUR OWN implementation (NO dependencies!)
// Unix library loading via dlopen - OUR OWN implementation (NO dependencies!)
//
// Status: ✅ FULLY WORKING
// ✅ syscall6 (internal/syscall) - Core C function calls (~30ns overhead)
Expand Down
2 changes: 1 addition & 1 deletion ffi/dl_windows.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build windows && amd64
//go:build windows

package ffi

Expand Down
2 changes: 1 addition & 1 deletion ffi/fakecgo_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && !cgo && !nofakecgo
//go:build (linux || darwin || freebsd) && !cgo && !nofakecgo

package ffi

Expand Down
2 changes: 1 addition & 1 deletion internal/arch/amd64/call_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build amd64 && (linux || darwin)
//go:build amd64 && (linux || darwin || freebsd)

// Unix implementation using System V AMD64 ABI (Linux, macOS, FreeBSD, etc.)
// This implementation closely follows purego's proven approach but is OUR OWN code.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//go:build arm64 && (linux || darwin)
//go:build arm64 && (linux || darwin || windows)

// Unix implementation using AAPCS64 ABI (Linux, macOS on ARM64)
// This implementation follows the ARM64 Procedure Call Standard.
// AAPCS64 ABI implementation (Linux, macOS, Windows on ARM64)
// Windows ARM64 uses the same calling convention as Unix ARM64 for non-variadic functions.
// See: https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions

package arm64

Expand Down
30 changes: 30 additions & 0 deletions internal/dl/dl_freebsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//go:build freebsd

// FreeBSD-specific constants for dynamic library loading.
//
// FreeBSD uses the same POSIX dlopen/dlsym API as Linux and macOS.
// RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_LAZY match Linux values.
// RTLD_DEFAULT matches macOS value (not Linux).
//
// Reference: https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h

package dl

// RTLD constants from <dlfcn.h> for dynamic library loading on FreeBSD.
const (
// RTLD_LAZY performs relocations at an implementation-dependent time.
RTLD_LAZY = 0x00001

// RTLD_NOW resolves all symbols when loading the library (recommended).
RTLD_NOW = 0x00002

// RTLD_GLOBAL makes all symbols available for relocation processing of other modules.
RTLD_GLOBAL = 0x00100

// RTLD_LOCAL makes symbols not available for relocation processing by other modules.
RTLD_LOCAL = 0x00000
)

// RTLD_DEFAULT is a pseudo-handle for dlsym to search for any loaded symbol.
// Same value as macOS, different from Linux (0x00000).
const RTLD_DEFAULT = 1<<64 - 2 // -2 as uintptr
15 changes: 15 additions & 0 deletions internal/dl/dl_freebsd_nocgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build freebsd && !cgo

package dl

// Link to libc.so.7 functions using cgo_import_dynamic.
// On FreeBSD, dlopen/dlsym/dlclose are part of libc directly
// (unlike Linux where they're in a separate libdl.so.2).

//go:cgo_import_dynamic goffi_dlopen dlopen "libc.so.7"
//go:cgo_import_dynamic goffi_dlsym dlsym "libc.so.7"
//go:cgo_import_dynamic goffi_dlerror dlerror "libc.so.7"
//go:cgo_import_dynamic goffi_dlclose dlclose "libc.so.7"

// Force dependency on libc.so.7
//go:cgo_import_dynamic _ _ "libc.so.7"
2 changes: 1 addition & 1 deletion internal/dl/dl_stubs_arm64.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && arm64 && !cgo
//go:build (linux || darwin || freebsd) && arm64 && !cgo

#include "textflag.h"

Expand Down
2 changes: 1 addition & 1 deletion internal/dl/dl_stubs_unix.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && amd64 && !cgo
//go:build (linux || darwin || freebsd) && amd64 && !cgo

#include "textflag.h"

Expand Down
2 changes: 1 addition & 1 deletion internal/dl/dl_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && !cgo
//go:build (linux || darwin || freebsd) && !cgo

// OUR OWN Dlopen/Dlsym implementation - NO dependencies!
// Uses runtime.cgocall approach similar to syscall6.
Expand Down
2 changes: 1 addition & 1 deletion internal/dl/dl_wrappers_arm64.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && arm64 && !cgo
//go:build (linux || darwin || freebsd) && arm64 && !cgo

#include "textflag.h"

Expand Down
2 changes: 1 addition & 1 deletion internal/dl/dl_wrappers_unix.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && amd64 && !cgo
//go:build (linux || darwin || freebsd) && amd64 && !cgo

#include "textflag.h"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//go:build (linux || darwin) && arm64
//go:build (linux || darwin || windows) && arm64

// AAPCS64 ABI syscall implementation (Linux, macOS on ARM64)
// ARM64 Procedure Call Standard - identical on all Unix-like systems.
// AAPCS64 ABI syscall implementation (Linux, macOS, Windows on ARM64)
// ARM64 Procedure Call Standard - identical across all platforms.
package syscall

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && arm64
//go:build (linux || darwin || windows) && arm64

#include "textflag.h"
#include "abi_arm64.h"
Expand Down
2 changes: 1 addition & 1 deletion internal/syscall/syscall_unix_amd64.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && amd64
//go:build (linux || darwin || freebsd) && amd64

// System V AMD64 ABI syscall implementation (Linux, macOS, FreeBSD, etc.)
// This calling convention is IDENTICAL on all Unix-like systems.
Expand Down
2 changes: 1 addition & 1 deletion internal/syscall/syscall_unix_amd64.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin) && amd64
//go:build (linux || darwin || freebsd) && amd64

#include "textflag.h"
#include "abi_amd64.h"
Expand Down
Loading