| // 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. | |
| //go:build goexperiment.simd && (amd64 || arm64 || wasm) | |
| package simd | |
| import ( | |
| "fmt" | |
| "internal/godebug" | |
| "strconv" | |
| ) | |
| // The `simd` package provides an architecture and vector-length agnostic API | |
| // for single-instruction-multiple-data "SIMD" vectors and operations. The | |
| // functions and methods in this package are those that can be mostly supported | |
| // in hardware, combined with an emulation for those platforms that are not yet | |
| // supported. | |
| // | |
| // Users can also control emulation and vector length with the 'simd' GODEBUG | |
| // setting. GODEBUG=simd=0 requests emulation, not hardware SIMD, even if | |
| // hardware is available. On platforms that may support multiple vector | |
| // lengths, GODEBUG=simd=N (N=128, 256, or 512) requests a specific vector | |
| // length. If the request cannot be satisfied, the simd package panics | |
| // informatively. | |
| // | |
| // Some platforms may support vectors of a particular length, but not all of the | |
| // expected operations (those appearing in this package) are available at that | |
| // length. In that case, the default is to automatically downgrade to a length | |
| // where the operations are supported, perhaps even to emulated-only | |
| // (size=0). If a size is requested that is not compatible with the available | |
| // features, the simd package will panic (and note the reason). To override | |
| // the feature check, in the case that the user knows that the missing | |
| // operations will not be used, prefix the size request with a '+', for | |
| // example "GODEBUG=simd=+256". A plain '+' will override the feature check at | |
| // whatever the hardware's default vector size happens to be. | |
| var simd = godebug.New("#simd") | |
| var maxVectorSize int | |
| var emulated = false | |
| var hwClmul = true | |
| func init() { | |
| actualMax, allFeatureSize := archMaxVectorSize() // zero == no simd, zero == features unavailable | |
| maxVectorSize, emulated, hwClmul = configure(actualMax, allFeatureSize, simd.Value()) | |
| } | |
| func configure(actualMax, allFeatureSize int, gosimd string) (maxVectorSize int, emulated, hwClmul bool) { | |
| explicitRequest := false | |
| hwClmul = true | |
| // No SIMD, must emulate | |
| if actualMax == 0 { | |
| return 128, true, false | |
| } | |
| maxVectorSize = actualMax | |
| // If gosimd begins with a '+' or is a single '1' then override | |
| // any hardware feature check disabling of hardware SIMD. | |
| // The '+' may be followed by a size, expected to be 0, 128, 256, 512. | |
| // If it is zero (e.g., "0" or +0") then hardware SIMD is still disabled. | |
| if gosimd == "1" { | |
| gosimd = "+" | |
| } | |
| if len(gosimd) > 0 && gosimd[0] == '+' { | |
| // override feature reduction | |
| // keep maxVectorSize | |
| // emulated remains false | |
| // note if features missing. | |
| hwClmul = allFeatureSize >= actualMax | |
| gosimd = gosimd[1:] | |
| explicitRequest = true | |
| } else if allFeatureSize < actualMax { | |
| if allFeatureSize > 0 { | |
| maxVectorSize = allFeatureSize | |
| hwClmul = true | |
| emulated = false | |
| } else { | |
| maxVectorSize = 128 | |
| hwClmul = false | |
| emulated = true | |
| } | |
| } | |
| if gosimd == "" { | |
| return maxVectorSize, emulated, hwClmul | |
| } | |
| // possible adjustment to chosen size | |
| val, err := strconv.Atoi(gosimd) | |
| if err != nil { | |
| panic(fmt.Errorf("could not parse GODEBUG=simd='%s' as a decimal number: %v", gosimd, err)) | |
| } | |
| if val < 0 { | |
| panic(fmt.Errorf("requested GODEBUG=simd=%d is negative", val)) | |
| } | |
| if val != 0 && val != 128 && val != 256 && val != 512 { | |
| panic(fmt.Errorf("requested GODEBUG=simd=%d is not a supported vector size (want 0, 128, 256, or 512)", val)) | |
| } | |
| if val > actualMax { | |
| panic(fmt.Errorf("requested GODEBUG=simd=%d is larger than the SIMD length (%d) supported on this CPU", val, actualMax)) | |
| } | |
| if !explicitRequest && val > allFeatureSize { | |
| panic(fmt.Errorf("requested GODEBUG=simd=%d is larger than the SIMD length required for expected features (%d) on this CPU; GODEBUG=simd=+%d will skip this check", val, allFeatureSize, val)) | |
| } | |
| // user-requested emulation | |
| if val == 0 { | |
| maxVectorSize = 128 | |
| hwClmul = false | |
| emulated = true | |
| return maxVectorSize, emulated, hwClmul | |
| } | |
| hwClmul = allFeatureSize >= val | |
| maxVectorSize = val | |
| emulated = false | |
| return maxVectorSize, emulated, hwClmul | |
| } | |
| // VectorBitSize returns the bit length of the longest vector available | |
| // on the current hardware. It can be artificially reduced by setting | |
| // GODEBUG=simd=<smaller size> environment variable before running a program. | |
| func VectorBitSize() int { | |
| return maxVectorSize | |
| } | |
| // Emulated returns whether simd operations are emulated or | |
| // running on actual vector hardware. | |
| func Emulated() bool { | |
| return emulated | |
| } | |
| // HasHardwareCarrylessMultiply returns whether this platform | |
| // has a hardware-implemented version of carryless multiply. | |
| // With default GODEBUG=simd settings, if this is false, | |
| // it is emulated and merely slow, but with non-default settings | |
| // this can indicate the possibility of a missing instruction | |
| // that will fail ("SIGILL") if it is executed. | |
| func HasHardwareCarrylessMultiply() bool { | |
| return hwClmul && archHasHwClmul | |
| } |