Contributing to simd
August 1, 2026 · View on GitHub
Thank you for your interest in contributing to the SIMD library! This document provides guidelines and information for contributors.
Getting Started
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/simd.git - Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes
- Run tests:
go test ./... - Run linter:
golangci-lint run - Commit your changes with a descriptive message
- Push to your fork and submit a pull request
Development Setup
Prerequisites
- Go 1.25 or later
- GCC (for C reference implementation tests)
- golangci-lint (for linting)
Building and Testing
# Run all tests
go test ./...
# Run tests with verbose output
go test -v ./...
# Run benchmarks
go test ./f64 -bench=. -benchmem
go test ./f32 -bench=. -benchmem
# Run linter
golangci-lint run
# Differential fuzzing: each SIMD path is fuzzed against its pure-Go reference
# (fuzz_test.go in f32, f64, i32, crc, c64, c128, i16, f16). The seed corpus runs
# as part of `go test ./...`; to actively fuzz a single target, run for example:
go test ./f32 -run '^$' -fuzz '^FuzzF32Reductions$' -fuzztime=30s
go test ./i32 -run '^$' -fuzz '^FuzzI32LPC$' -fuzztime=30s
go test ./c64 -run '^$' -fuzz '^FuzzC64DotProduct$' -fuzztime=30s
go test ./f16 -run '^$' -fuzz '^FuzzF16RoundTrip$' -fuzztime=30s
# Generate test expectations from C reference
cd testdata
gcc -O2 -march=native -o generate_expectations generate_expectations.c -lm
./generate_expectations
Code Organization
simd/
├── cpu/ # CPU feature detection
│ ├── cpu.go # Common types and functions
│ ├── cpu_amd64.go
│ ├── cpu_arm64.go
│ └── cpu_other.go
├── f64/ # float64 operations
│ ├── f64.go # Public API
│ ├── f64_go.go # Pure Go implementations
│ ├── f64_amd64.go # AMD64 dispatchers
│ ├── f64_amd64.s # AMD64 assembly
│ ├── f64_arm64.go # ARM64 dispatchers
│ ├── f64_arm64.s # ARM64 assembly
│ ├── f64_other.go # Fallback dispatchers
│ └── *_test.go # Tests and benchmarks
├── f32/ # float32 operations (same structure)
├── c128/ # complex128 operations (same structure)
├── testdata/ # C reference implementation
├── .githooks/ # Git pre-commit hooks
├── Taskfile.yml # Task runner configuration
├── doc.go # Package documentation
├── README.md
├── CONTRIBUTING.md
├── LICENSE
└── go.mod
Adding New Operations
When adding a new SIMD operation, follow these steps:
1. Add the Public API (f64.go / f32.go)
// OperationName performs [description].
// Processes min(len(dst), len(a)) elements.
func OperationName(dst, a []float64) {
n := min(len(dst), len(a))
if n == 0 {
return
}
operationName64(dst[:n], a[:n])
}
2. Add Pure Go Implementation (f64_go.go)
func operationNameGo(dst, a []float64) {
for i := range dst {
dst[i] = // operation
}
}
3. Add Dispatchers
For AMD64 (f64_amd64.go):
func operationName64(dst, a []float64) {
if hasAVX && len(dst) >= 4 {
operationNameAVX(dst, a)
return
}
operationNameGo(dst, a)
}
//go:noescape
func operationNameAVX(dst, a []float64)
For ARM64 (f64_arm64.go):
func operationName64(dst, a []float64) {
if hasNEON && len(dst) >= 2 {
operationNameNEON(dst, a)
return
}
operationNameGo(dst, a)
}
//go:noescape
func operationNameNEON(dst, a []float64)
For other architectures (f64_other.go):
func operationName64(dst, a []float64) { operationNameGo(dst, a) }
4. Add Assembly Implementation
Add to f64_amd64.s and f64_arm64.s. For initial development, you can add a stub:
TEXT ·operationNameAVX(SB), \$0-48
JMP ·operationNameGo(SB)
5. Add Tests
Add tests in f64_test.go or create a new test file:
func TestOperationName(t *testing.T) {
tests := []struct {
name string
a []float64
want []float64
}{
{"empty", nil, nil},
{"single", []float64{1}, []float64{1}},
// Add more test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dst := make([]float64, len(tt.a))
OperationName(dst, tt.a)
// Assert results
})
}
}
6. Update C Reference
Add the operation to testdata/generate_expectations.c for validation.
7. Update Documentation
Update README.md to include the new operation in the API table, and add it to
the package index in doc.go. Both are hand-maintained and nothing checks them,
so an operation added to one and not the other drifts silently; that is how the
FFT primitives ended up in the README but not in doc.go.
Code Style
- Follow standard Go formatting (
gofmt) - Use meaningful variable names
- Add doc comments for all public functions
- Keep functions focused and small
- Avoid magic numbers - use named constants
Assembly Guidelines
- Use Go's plan9 assembly syntax
- Include comments explaining the algorithm
- Handle remainder elements after SIMD loops
- Use
VZEROUPPERat the end of AVX functions (AMD64) - Test with various slice sizes to exercise all code paths
Before hand-encoding a NEON instruction as a WORD, or reaching for an unfamiliar
Plan 9 mnemonic, read docs/assembly-encoding.md: it
lists which arm64 integer-multiply instructions the assembler rejects (so must be
WORD-encoded), the amd64 spellings that differ from Intel (PMADDWD is
PMADDWL), and how to verify a new encoding without aarch64 binutils.
Pull Request Guidelines
- Title: Use a clear, descriptive title
- Description: Explain what the PR does and why
- Tests: Include tests for new functionality
- Benchmarks: Include benchmarks for performance-critical code
- Documentation: Update README if adding new features
Reporting Issues
When reporting issues, please include:
- Go version (
go version) - Operating system and architecture
- Steps to reproduce
- Expected vs actual behavior
- Relevant code snippets
Questions?
Feel free to open an issue for questions or discussions about potential contributions.