github.com/primecitizens/pcz/std@v0.2.1/core/cpu/cpu_x86.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 // 4 // Copyright 2017 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 //go:build 386 || amd64 9 10 package cpu 11 12 import "unsafe" 13 14 const CacheLinePadSize = 64 15 16 // cpuid is implemented in cpu_x86.s. 17 func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) 18 19 // xgetbv with ecx = 0 is implemented in cpu_x86.s. 20 func xgetbv() (eax, edx uint32) 21 22 // getGOAMD64level is implemented in cpu_x86.s. Returns number in [1,4]. 23 func getGOAMD64level() int32 24 25 const ( 26 // edx bits 27 cpuid_SSE2 = 1 << 26 28 29 // ecx bits 30 cpuid_SSE3 = 1 << 0 31 cpuid_PCLMULQDQ = 1 << 1 32 cpuid_SSSE3 = 1 << 9 33 cpuid_FMA = 1 << 12 34 cpuid_SSE41 = 1 << 19 35 cpuid_SSE42 = 1 << 20 36 cpuid_POPCNT = 1 << 23 37 cpuid_AES = 1 << 25 38 cpuid_OSXSAVE = 1 << 27 39 cpuid_AVX = 1 << 28 40 41 // ebx bits 42 cpuid_BMI1 = 1 << 3 43 cpuid_AVX2 = 1 << 5 44 cpuid_BMI2 = 1 << 8 45 cpuid_ERMS = 1 << 9 46 cpuid_ADX = 1 << 19 47 48 // edx bits for CPUID 0x80000001 49 cpuid_RDTSCP = 1 << 27 50 ) 51 52 var ( 53 maxExtendedFunctionInformation uint32 54 ) 55 56 func doinit() (ret *X86Features) { 57 ret = &X86 58 maxID, _, _, _ := cpuid(0, 0) 59 60 if maxID < 1 { 61 return 62 } 63 64 maxExtendedFunctionInformation, _, _, _ = cpuid(0x80000000, 0) 65 66 _, _, ecx1, _ := cpuid(1, 0) 67 68 if isSet(ecx1, cpuid_SSE3) { 69 X86 |= X86Feature_sse3 70 } 71 if isSet(ecx1, cpuid_PCLMULQDQ) { 72 X86 |= X86Feature_pclmulqdq 73 } 74 if isSet(ecx1, cpuid_SSSE3) { 75 X86 |= X86Feature_ssse3 76 } 77 if isSet(ecx1, cpuid_SSE41) { 78 X86 |= X86Feature_sse41 79 } 80 if isSet(ecx1, cpuid_SSE42) { 81 X86 |= X86Feature_sse42 82 } 83 if isSet(ecx1, cpuid_POPCNT) { 84 X86 |= X86Feature_popcnt 85 } 86 if isSet(ecx1, cpuid_AES) { 87 X86 |= X86Feature_aes 88 } 89 90 // OSXSAVE can be false when using older Operating Systems 91 // or when explicitly disabled on newer Operating Systems by 92 // e.g. setting the xsavedisable boot option on Windows 10. 93 HasOSXSAVE := isSet(ecx1, cpuid_OSXSAVE) 94 95 // The FMA instruction set extension only has VEX prefixed instructions. 96 // VEX prefixed instructions require OSXSAVE to be enabled. 97 // See Intel 64 and IA-32 Architecture Software Developer’s Manual Volume 2 98 // Section 2.4 "AVX and SSE Instruction Exception Specification" 99 if HasOSXSAVE && isSet(ecx1, cpuid_FMA) { 100 X86 |= X86Feature_fma 101 } 102 103 osSupportsAVX := false 104 // For XGETBV, OSXSAVE bit is required and sufficient. 105 if HasOSXSAVE { 106 eax, _ := xgetbv() 107 // Check if XMM and YMM registers have OS support. 108 osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2) 109 } 110 111 if osSupportsAVX && isSet(ecx1, cpuid_AVX) { 112 X86 |= X86Feature_avx 113 } 114 115 if maxID < 7 { 116 return 117 } 118 119 _, ebx7, _, _ := cpuid(7, 0) 120 if isSet(ebx7, cpuid_BMI1) { 121 X86 |= X86Feature_bmi1 122 } 123 124 if osSupportsAVX && isSet(ebx7, cpuid_AVX2) { 125 X86 |= X86Feature_avx2 126 } 127 128 if isSet(ebx7, cpuid_BMI2) { 129 X86 |= X86Feature_bmi2 130 } 131 132 if isSet(ebx7, cpuid_ERMS) { 133 X86 |= X86Feature_erms 134 } 135 136 if isSet(ebx7, cpuid_ADX) { 137 X86 |= X86Feature_adx 138 } 139 140 var maxExtendedInformation uint32 141 maxExtendedInformation, _, _, _ = cpuid(0x80000000, 0) 142 143 if maxExtendedInformation < 0x80000001 { 144 return 145 } 146 147 _, _, _, edxExt1 := cpuid(0x80000001, 0) 148 if isSet(edxExt1, cpuid_RDTSCP) { 149 X86 |= X86Feature_rdtscp 150 } 151 return 152 } 153 154 func isSet(hwc uint32, value uint32) bool { 155 return hwc&value != 0 156 } 157 158 var nameBuf [3 * 4 * 4]byte 159 160 // Name returns the CPU name given by the vendor. 161 // If the CPU name can not be determined an 162 // empty string is returned. 163 func Name() string { 164 if maxExtendedFunctionInformation < 0x80000004 { 165 return "" 166 } 167 168 data := nameBuf[:] 169 170 var eax, ebx, ecx, edx uint32 171 eax, ebx, ecx, edx = cpuid(0x80000002, 0) 172 data = appendBytes(data, eax, ebx, ecx, edx) 173 eax, ebx, ecx, edx = cpuid(0x80000003, 0) 174 data = appendBytes(data, eax, ebx, ecx, edx) 175 eax, ebx, ecx, edx = cpuid(0x80000004, 0) 176 data = appendBytes(data, eax, ebx, ecx, edx) 177 178 // Trim leading spaces. 179 for len(data) > 0 && data[0] == ' ' { 180 data = data[1:] 181 } 182 183 // Trim tail after and including the first null byte. 184 for i, c := range data { 185 if c == '\x00' { 186 data = data[:i] 187 break 188 } 189 } 190 191 return *(*string)(unsafe.Pointer(&data)) 192 } 193 194 func appendBytes(b []byte, args ...uint32) []byte { 195 for _, arg := range args { 196 b = append(b, 197 byte((arg >> 0)), 198 byte((arg >> 8)), 199 byte((arg >> 16)), 200 byte((arg >> 24))) 201 } 202 return b 203 }