| // Copyright 2017 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 cpu implements processor feature detection |
| // used by the Go standard library. |
| package cpu |
| |
| // DebugOptions is set to true by the runtime if the OS supports reading |
| // GODEBUG early in runtime startup. |
| // This should not be changed after it is initialized. |
| var DebugOptions bool |
| |
| // CacheLinePad is used to pad structs to avoid false sharing. |
| type CacheLinePad struct{ _ [CacheLinePadSize]byte } |
| |
| // CacheLineSize is the CPU's assumed cache line size. |
| // There is currently no runtime detection of the real cache line size |
| // so we use the constant per GOARCH CacheLinePadSize as an approximation. |
| var CacheLineSize uintptr = CacheLinePadSize |
| |
| // The booleans in X86 contain the correspondingly named cpuid feature bit. |
| // HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers |
| // in addition to the cpuid feature bit being set. |
| // The struct is padded to avoid false sharing. |
| var X86 struct { |
| _ CacheLinePad |
| HasAES bool |
| HasADX bool |
| HasAVX bool |
| HasAVX2 bool |
| HasBMI1 bool |
| HasBMI2 bool |
| HasERMS bool |
| HasFMA bool |
| HasOSXSAVE bool |
| HasPCLMULQDQ bool |
| HasPOPCNT bool |
| HasSSE2 bool |
| HasSSE3 bool |
| HasSSSE3 bool |
| HasSSE41 bool |
| HasSSE42 bool |
| _ CacheLinePad |
| } |
| |
| // The booleans in ARM contain the correspondingly named cpu feature bit. |
| // The struct is padded to avoid false sharing. |
| var ARM struct { |
| _ CacheLinePad |
| HasVFPv4 bool |
| HasIDIVA bool |
| _ CacheLinePad |
| } |
| |
| // The booleans in ARM64 contain the correspondingly named cpu feature bit. |
| // The struct is padded to avoid false sharing. |
| var ARM64 struct { |
| _ CacheLinePad |
| HasAES bool |
| HasPMULL bool |
| HasSHA1 bool |
| HasSHA2 bool |
| HasCRC32 bool |
| HasATOMICS bool |
| HasCPUID bool |
| IsNeoverseN1 bool |
| IsZeus bool |
| _ CacheLinePad |
| } |
| |
| var MIPS64X struct { |
| _ CacheLinePad |
| HasMSA bool // MIPS SIMD architecture |
| _ CacheLinePad |
| } |
| |
| // For ppc64(le), it is safe to check only for ISA level starting on ISA v3.00, |
| // since there are no optional categories. There are some exceptions that also |
| // require kernel support to work (darn, scv), so there are feature bits for |
| // those as well. The minimum processor requirement is POWER8 (ISA 2.07). |
| // The struct is padded to avoid false sharing. |
| var PPC64 struct { |
| _ CacheLinePad |
| HasDARN bool // Hardware random number generator (requires kernel enablement) |
| HasSCV bool // Syscall vectored (requires kernel enablement) |
| IsPOWER8 bool // ISA v2.07 (POWER8) |
| IsPOWER9 bool // ISA v3.00 (POWER9) |
| _ CacheLinePad |
| } |
| |
| var S390X struct { |
| _ CacheLinePad |
| HasZARCH bool // z architecture mode is active [mandatory] |
| HasSTFLE bool // store facility list extended [mandatory] |
| HasLDISP bool // long (20-bit) displacements [mandatory] |
| HasEIMM bool // 32-bit immediates [mandatory] |
| HasDFP bool // decimal floating point |
| HasETF3EH bool // ETF-3 enhanced |
| HasMSA bool // message security assist (CPACF) |
| HasAES bool // KM-AES{128,192,256} functions |
| HasAESCBC bool // KMC-AES{128,192,256} functions |
| HasAESCTR bool // KMCTR-AES{128,192,256} functions |
| HasAESGCM bool // KMA-GCM-AES{128,192,256} functions |
| HasGHASH bool // KIMD-GHASH function |
| HasSHA1 bool // K{I,L}MD-SHA-1 functions |
| HasSHA256 bool // K{I,L}MD-SHA-256 functions |
| HasSHA512 bool // K{I,L}MD-SHA-512 functions |
| HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions |
| HasVX bool // vector facility. Note: the runtime sets this when it processes auxv records. |
| HasVXE bool // vector-enhancements facility 1 |
| HasKDSA bool // elliptic curve functions |
| HasECDSA bool // NIST curves |
| HasEDDSA bool // Edwards curves |
| _ CacheLinePad |
| } |
| |
| // Initialize examines the processor and sets the relevant variables above. |
| // This is called by the runtime package early in program initialization, |
| // before normal init functions are run. env is set by runtime if the OS supports |
| // cpu feature options in GODEBUG. |
| func Initialize(env string) { |
| doinit() |
| processOptions(env) |
| } |
| |
| // options contains the cpu debug options that can be used in GODEBUG. |
| // Options are arch dependent and are added by the arch specific doinit functions. |
| // Features that are mandatory for the specific GOARCH should not be added to options |
| // (e.g. SSE2 on amd64). |
| var options []option |
| |
| // Option names should be lower case. e.g. avx instead of AVX. |
| type option struct { |
| Name string |
| Feature *bool |
| Specified bool // whether feature value was specified in GODEBUG |
| Enable bool // whether feature should be enabled |
| Required bool // whether feature is mandatory and can not be disabled |
| } |
| |
| // processOptions enables or disables CPU feature values based on the parsed env string. |
| // The env string is expected to be of the form cpu.feature1=value1,cpu.feature2=value2... |
| // where feature names is one of the architecture specific list stored in the |
| // cpu packages options variable and values are either 'on' or 'off'. |
| // If env contains cpu.all=off then all cpu features referenced through the options |
| // variable are disabled. Other feature names and values result in warning messages. |
| func processOptions(env string) { |
| field: |
| for env != "" { |
| field := "" |
| i := indexByte(env, ',') |
| if i < 0 { |
| field, env = env, "" |
| } else { |
| field, env = env[:i], env[i+1:] |
| } |
| if len(field) < 4 || field[:4] != "cpu." { |
| continue |
| } |
| i = indexByte(field, '=') |
| if i < 0 { |
| print("GODEBUG: no value specified for \"", field, "\"\n") |
| continue |
| } |
| key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on" |
| |
| var enable bool |
| switch value { |
| case "on": |
| enable = true |
| case "off": |
| enable = false |
| default: |
| print("GODEBUG: value \"", value, "\" not supported for cpu option \"", key, "\"\n") |
| continue field |
| } |
| |
| if key == "all" { |
| for i := range options { |
| options[i].Specified = true |
| options[i].Enable = enable || options[i].Required |
| } |
| continue field |
| } |
| |
| for i := range options { |
| if options[i].Name == key { |
| options[i].Specified = true |
| options[i].Enable = enable |
| continue field |
| } |
| } |
| |
| print("GODEBUG: unknown cpu feature \"", key, "\"\n") |
| } |
| |
| for _, o := range options { |
| if !o.Specified { |
| continue |
| } |
| |
| if o.Enable && !*o.Feature { |
| print("GODEBUG: can not enable \"", o.Name, "\", missing CPU support\n") |
| continue |
| } |
| |
| if !o.Enable && o.Required { |
| print("GODEBUG: can not disable \"", o.Name, "\", required CPU feature\n") |
| continue |
| } |
| |
| *o.Feature = o.Enable |
| } |
| } |
| |
| // indexByte returns the index of the first instance of c in s, |
| // or -1 if c is not present in s. |
| func indexByte(s string, c byte) int { |
| for i := 0; i < len(s); i++ { |
| if s[i] == c { |
| return i |
| } |
| } |
| return -1 |
| } |