| # The Go SIMD code generators | |
| This directory contains the code generators for Go's SIMD support, along with | |
| the libraries and debugging tools they are built from. It is a separate module | |
| from the main Go tree. `main.go` is the driver that runs all the generators, or | |
| they can be run individually. | |
| We generate for four **targets**: amd64, arm64 (NEON), arm64 SVE, and wasm. SVE | |
| is not a GOARCH — it is an arm64 feature with its own generated files, its own | |
| scalable vector types, and its own simdgen invocation. It behaves like a | |
| separate target throughout, so "four targets" always means these four. | |
| ## The packages we generate | |
| The Go SIMD API lives under `$GOROOT/src/simd`: | |
| | Path | What it is | | |
| |---|---| | |
| | `simd/` | Portable package. Length-agnostic vector types only (`Int8s`, `Float32s`, `Mask8s`, …). Works on every platform. | | |
| | `simd/archsimd/` | Platform package. Fixed-width types (`Int8x16` … `Float64x8`) plus scalable types on SVE. Contents vary by GOARCH. | | |
| | `simd/internal/spec/` | The executable specification. Buildable Go, but not meant to be called directly. | | |
| | `simd/internal/simdref/` | Reference implementation of the API, generated from spec by `refgen`. | | |
| | `simd/internal/bridge/` | Glue between `simd` and `archsimd`. Generated by `midway`, not user-visible. | | |
| ## The "one meaning" rule | |
| Both `simd` and `archsimd` follow a single rule, which is the hardest-won | |
| property of the whole project: | |
| > **One name means one thing.** A name need not exist on every platform or in | |
| > both packages, but wherever it does exist it must denote the same operation, | |
| > with the same signature and the same semantics. | |
| A method must mean one thing *across all types* within a category, where the | |
| only two categories are vectors and masks. Furthermore, its signature should | |
| vary only in minor ways that depend on the receiver (e.g., it may be sensitive | |
| to the number of lanes). `simd` and `archsimd` even have exactly the same | |
| scalable vector types. On fixed-width architectures, the types differ, but | |
| method names, signatures, and meanings still match. | |
| Method names shared *between categories* should at least be logically related. | |
| For example, `Mask8x16.ToBits()` yields a `uint16` bitmap while | |
| `Float32x4.ToBits()` reinterprets the bits as `Uint32x4`. Different shapes, | |
| plainly related meaning, both correct. | |
| One caveat is that implementations of a method may differ in minor, | |
| hardware-dependent ways, such as floating point rounding and treatment of | |
| out-of-range conversions. The allowed wiggle room is not yet well-defined. | |
| Historically the rule was maintained by | |
| [`apisum`](https://github.com/aclements/go-misc/tree/master/simdapi/cmd/apisum), | |
| which audits after the fact. `spec` is the same idea in reverse — consistent by | |
| construction rather than by observation — and it doubles as documentation and a | |
| behavioral oracle. | |
| ## The intersection corollary | |
| The `simd` package API is the intersection of the `archsimd` package APIs across | |
| all architectures, after generalizing fixed-width to scalable width, plus a few | |
| methods for querying emulation behavior. This is only possible because of the | |
| "one meaning" rule. | |
| Another consequence of this: if we want to add an operation to `simd`, we do so | |
| by ensuring it's implemented in `archsimd` across all architectures, clearly | |
| documenting where it's an emulation in the `archsimd` package. | |
| The key constraint of the `simd` surface is that every operation must be either | |
| supported natively or efficient to emulate on every architecture. In general, we | |
| follow the Highway project's lead on what operations can be universally | |
| efficient. | |
| ## The generators | |
| | Directory | Role | | |
| |---|---| | |
| | `simdgen/` | The big one. Unification-based. Produces the amd64/arm64/SVE API *and* the compiler backend. | | |
| | `tmplgen/` | Template expansion for slice ops, compares, mask merges, conversions, and testing boilerplate. | | |
| | `wasmgen/` | The wasm API. | | |
| | `midway/` | Computes the intersection of methods across architectures to produce the portable `simd` package's declarations. | | |
| | `specgen/` | Interprets `internal/spec` into a concrete API description. | | |
| | `specgen/specexpr/` | Constraint solver over vector shapes. | | |
| | `unify/` | The unification engine simdgen is built on. | | |
| | `gentools/` | General shared file-writing utility: `-w`, `-diff`, `-txtar`, `-goroot`. Used by every generator. | | |
| | `sgutil/` | Assorted helpers specific to these generators: path resolution, ordered maps, natural sort. | | |
| | `cmd/refgen/` | Generates `internal/simdref` from spec. | | |
| | `cmd/specls/` | Not a generator: prints the API that spec expands to. The main debugging tool for spec. | | |
| The best way to see what files in the simd packages are generated and by what | |
| tools is `grep -r '^// Code generated by'`. In addition, simdgen generates the | |
| compiler's `ssa/_gen/simd*ops.go`, `simd*.rules`, and | |
| `ssagen/simd*intrinsics.go` files. | |
| ## How simdgen works | |
| simdgen unifies four inputs, decodes the unified results into `Operation` | |
| values, performs several canonicalization and update steps, then runs several | |
| emitters over the final set of `Operation`s. | |
| | Input | Content | | |
| |---|---| | |
| | Platform loader (`xed.go`, `arm64/`, `sve/`) | Machine instructions parsed from ISA tables into `simdgen` defs | | |
| | `types.yaml` | Vocabulary of legal types as `simdgen` defs | | |
| | `ops/*/categories.yaml` | Platform-independent API defs: name, doc, commutativity | | |
| | `ops/*/go_GOARCH.yaml` | Glue between the ISA defs and Go API: `go:` (often a regexp), `asm:` pattern, operand patterns, and a long tail of quirk fields | | |
| Two structural facts that are easy to miss: | |
| - `!import ops/*/categories.yaml` produces a **single global sum**. The | |
| `ops/AddSub/`, `ops/Moves/` directory structure is cosmetic. Nothing scopes a | |
| directory's category entries to that directory's arch entries; everything | |
| cross-joins with everything. | |
| - `unify.Def` treats any absent field as `Top`, so **there is no schema**. Any | |
| invented or misspelled key is silently accepted and silently inert. Several | |
| keys in the checked-in YAML are dead weight for exactly this reason. | |
| The external instruction data is not checked in. Use `fetch-xed.sh` and | |
| `fetch-arm64.sh`, or point `-xedPath` / `-arm64Path` (or `$XEDPATH` / | |
| `$ARM64_ISA_PATH`) at existing copies. | |
| ## What spec and specgen are | |
| `internal/spec` describes operations as generic Go functions: | |
| ```go | |
| // Add adds corresponding elements of two vectors. | |
| // | |
| // z[i] = x[i] + y[i] | |
| func Add[E Nums, W Width](x, y Vec[E, W]) (z Vec[E, W]) { | |
| return map2[E, W, E, W](x, y, func(x, y E) E { return x + y }) | |
| } | |
| ``` | |
| `specgen.Load(dir string, opts *LoadOptions)` returns `([]*specgen.Func, | |
| error)`. Each `Func` is a **fully instantiated, concrete** API signature. | |
| `Recv`, `In`, and `Out` are `specgen.Arg` values — a parameter *name* plus a | |
| `specexpr.Type` — so a `Func` carries argument names as well as types, which | |
| matters to anything comparing signatures. There is also `Name` and `Doc`. Read | |
| `simd/internal/spec/doc.go` for the `//specgen:name`, `//specgen:require`, and | |
| `{{.var}}` doc-template mechanisms. | |
| Two properties are worth knowing before you build anything on specgen: | |
| 1. **`Func.Name` is already the API name.** `//specgen:name` is resolved inside | |
| specgen, so consumers see `ToBits` and `ReshapeToUint32s`, never the | |
| underlying spec function names. Matching a concrete API declaration to a spec | |
| function is a plain `(receiver type name, method name)` lookup. No mapping | |
| table is needed anywhere. | |
| 2. **`Func.Decl()` already composes doc + signature**, so anything that emits or | |
| compares declarations should build on it rather than reimplementing it. | |
| ## Working in this tree | |
| The `_gen` module requires at least the Go version listed in its `go.mod`. A | |
| working Go dev tree or an installed Go system at that version is required. | |
| Every generator supports `-w` (write into the tree), `-diff` (compare against | |
| the tree and print unified diffs), and `-txtar` (dump generated files to stdout; | |
| the default). **Use `-diff` constantly.** The generated files are checked in, so | |
| byte-for-byte comparison against them is available as a regression test for any | |
| change you make — including refactors that are supposed to change nothing. | |
| Be sure to include changes to generated outputs in the same commit that changes | |
| the generator. This makes it easy to review the effects of the generator change. | |
| Useful one-liners: | |
| ```sh | |
| go run ./cmd/specls # the API spec currently expands to | |
| go run ./cmd/specls -f Add # just one spec function | |
| go run ./cmd/specls -f Add -trace # why a spec function expands the way it does | |
| go run ./cmd/refgen -diff # is internal/simdref current? | |
| go run . -tools tmplgen -diff # run one generator via the driver | |
| ``` | |
| ## Reading order for someone new | |
| Working on spec? Check out: | |
| 1. `simd/internal/spec/doc.go` — the spec language: `Vec`, `Width`, | |
| `//specgen:name`, `//specgen:require`, doc templates. | |
| 2. `simd/internal/spec/math.go`, `loadstore.go`, `masks.go` — worked examples. | |
| 3. `specgen/api.go` — `Func`, `Signature()`, `Decl()`, `SpecFunc()`. | |
| 4. `specgen/specexpr/solver.go` — the shape constraint language, documented in | |
| the package comment. | |
| 5. `cmd/refgen/main.go` — a simple consumer of `specgen.Load`, and a model for | |
| how to walk `[]*Func`. | |
| Working on simdgen? Check out: | |
| 1. `simdgen/ops/AddSub/*.yaml` — unifier inputs for core math operations. | |
| 2. `simdgen/godefs.go` and `simdgen/types/operation.go` — the `Operation` model. |