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  }