| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package spec describes all possible operations in the SIMD API. |
| // |
| // The SIMD spec describes the function and method signatures, documentation |
| // comments, and behavior (written as a reference Go implementation) of all |
| // possible Go SIMD APIs. The archsimd and simd packages are subsets of this |
| // specified API. This approach enforces that "one name means one thing" across |
| // all platforms and both packages. |
| // |
| // The spec is written as buildable and executable Go code, but isn't meant to |
| // be called directly. Rather, the specgen package interprets this spec package |
| // into a full API description, which can then be fed into other generators for |
| // the actual SIMD packages. The executable part of the spec serves to precisely |
| // specify the semantics of operations, and is intended for conformance testing. |
| // |
| // To see the spec-generated API and debug issues with it, use [cmd/specls]. |
| // |
| // ## Basic operation specifications |
| // |
| // Spec operations are written in a stylized form that makes heavy use of type |
| // parameters so a single function can describe an operation generalized across |
| // many vector types. This is in contrast with the public SIMD API, where every |
| // function and method operates on concrete types. The specgen generator bridges |
| // this gap, instantiating a single parameterized spec function into many |
| // concrete methods. |
| // |
| // Consider a simple example, the Add operation: |
| // |
| // // Add adds corresponding elements of two vectors. |
| // func Add[E Nums, W Width](x, y Vec[E, W]) (z Vec[E, W]) { |
| // ... |
| // } |
| // |
| // All spec operations are written as functions, but if the first parameter has |
| // type Vec, then they specify a method of a vector type. Since Add's first |
| // parameter (x) is a Vec, this describes a method on vector types. |
| // |
| // The "E Nums" type parameter controls the allowed element types of the three |
| // vector types. Here, it can be any numeric type of any size (uint8, float64, |
| // etc). The "W Width" type parameter controls the total bit width of the three |
| // vector types. The types that implement Width stand in for 128, 256, or 512 |
| // bits, or "scalable", which can represent any power of two >= 128. The number |
| // of lanes of a vector is derived from the element size and the total vector |
| // width. |
| // |
| // The Add spec expands to all possible types that satisfy the E and W type |
| // parameters, which are in turn translated to types in the public API: |
| // |
| // func (Int8x16) Add(Int8x16) Int8x16 |
| // func (Int8x32) Add(Int8x32) Int8x32 |
| // func (Int8x64) Add(Int8x64) Int8x64 |
| // func (Int8s) Add(Int8s) Int8s |
| // ... |
| // func (Float64x8) Add(Float64x8) Float64x8 |
| // func (Float64s) Add(Float64s) Float64s |
| // |
| // ## Spec constraints |
| // |
| // For many operations, all possible combinations of their type parameters are |
| // valid, but some need to express constraints between type parameters that |
| // can't easily be described in the Go type system. For these, we support a |
| // `//specgen:requires` directive. Consider DotProductPairs: |
| // |
| // // DotProductPairs computes the dot product of x and y. |
| // // |
| // //specgen:require z={xB}{xN*2}x{xL/2} |
| // func DotProductPairs[E Nums, W Width, zE Nums](x, y Vec[E, W]) (z Vec[zE, W]) { |
| // ... |
| // } |
| // |
| // The require expression refers to the types of each parameter and result by |
| // name. For Vec (and Array) arguments, each parameter and result also get |
| // several related variables: |
| // |
| // v = The whole vector type (e.g., Uint32x8) |
| // vE = The element type (e.g., uint32) |
| // vB = The base type (e.g., uint) |
| // vN = The base type size (e.g., 32) |
| // vL = The number of lanes in the vector or elements in the array (e.g., 8) |
| // vW = The total bit width of the vector or array (e.g., 256) |
| // |
| // The full syntax for constraints is described in the [specgen/specexpr] |
| // package, but they often describe vector shapes like those in the |
| // DotProductPairs example. The form of these is: |
| // |
| // - BaseNxL, which describes a vector with L elements of type BaseN; |
| // - BaseNwW, which describes a vector of total width W; or |
| // - BaseNs, which describes a scalable vector of BaseN elements. |
| // |
| // Base, N, L, or W can be either a literal or an expression in {}'s. For |
| // example, Uint32x{vL} or {zB}{zN}w128. |
| // |
| // For DotProductPairs, the constraint "z={vB}{vN*2}x{vL/2}" says the result z |
| // must have the same base type as v, but z's element type must be twice as |
| // wide, and z must have half the number of lanes of v. |
| // |
| // The DotProductPairs constraint could also have been written in any of the |
| // following equivalent ways: |
| // |
| // z={vB}{vN*2}w{vW} Constrain the total vector width |
| // zB=vB zN=vN*2 zL=vL/2 Constrain each component separately |
| // zE={vB}{vN*2} zW=vW Constrain the element type and width separately |
| // |
| // ## Name and doc templates |
| // |
| // For some operations, the API name or documentation depends on type |
| // parameters. For these we support a simple template system where constraint |
| // variables can be referenced in curly braces, like {vE}, similar to {} |
| // expressions in shape constraints. For doc comments, these can be included |
| // directly in the doc comment. For names, we use a `//specgen:name` directive, |
| // such as |
| // |
| // //specgen:name Load{z} |
| // func LoadZ[E Elt, W Width](s []E) (z Vec[E, W]) { |
| // |
| // In this case, the spec function itself can be named anything (as long as it's |
| // exported), and the API name is generated from the directive. For example, |
| // when LoadZ is instantiated on uint32 and Width128, the API name generated |
| // from the template will be LoadUint32x4. This is particularly useful for |
| // constructor functions and conversion functions where types must appear in the |
| // name, such as LoadZ. |
| // |
| // ## The spec type system |
| // |
| // This package defines a set of types that translate to API types. We saw the |
| // [Vec] type above, which translates to a concrete BNxL (or BNs) vector type in |
| // the API, where L is determined from E and W. |
| // |
| // Masks are also represented using the [Vec] type, but with an element type |
| // from the [MaskElt] interface, such as Mask8, Mask16, etc. The generator |
| // translates these to Mask types in the API. Internal to the spec package, |
| // these are like a wide mask, where only 0 and ^0 are legal values for these |
| // elements. |
| // |
| // Similar to [Vec], there an [Array[E,W]] type that translates into a [L]E Go |
| // array type in the API. |
| // |
| // The type [UintN] stands for a uint type whose bit width is determined by spec |
| // constraints. |
| // |
| // Pointer and slice types translate directly to the API. |
| package spec |