github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/compress/libdeflate/cpu_features.c (about)

     1  /*
     2   * x86/cpu_features.c - feature detection for x86 processors
     3   *
     4   * Copyright 2016 Eric Biggers
     5   *
     6   * Permission is hereby granted, free of charge, to any person
     7   * obtaining a copy of this software and associated documentation
     8   * files (the "Software"), to deal in the Software without
     9   * restriction, including without limitation the rights to use,
    10   * copy, modify, merge, publish, distribute, sublicense, and/or sell
    11   * copies of the Software, and to permit persons to whom the
    12   * Software is furnished to do so, subject to the following
    13   * conditions:
    14   *
    15   * The above copyright notice and this permission notice shall be
    16   * included in all copies or substantial portions of the Software.
    17   *
    18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    19   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    20   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    21   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    22   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    23   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    24   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    25   * OTHER DEALINGS IN THE SOFTWARE.
    26   */
    27  
    28  #include "cpu_features.h"
    29  
    30  #if X86_CPU_FEATURES_ENABLED
    31  
    32  volatile u32 _cpu_features = 0;
    33  
    34  /* With old GCC versions we have to manually save and restore the x86_32 PIC
    35   * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602  */
    36  #if defined(__i386__) && defined(__PIC__)
    37  #  define EBX_CONSTRAINT "=r"
    38  #else
    39  #  define EBX_CONSTRAINT "=b"
    40  #endif
    41  
    42  /* Execute the CPUID instruction.  */
    43  static inline void
    44  cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
    45  {
    46  	__asm__(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
    47  		"cpuid                                  \n"
    48  		".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
    49  		: "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
    50  		: "a" (leaf), "c" (subleaf));
    51  }
    52  
    53  /* Read an extended control register.  */
    54  static inline u64
    55  read_xcr(u32 index)
    56  {
    57  	u32 edx, eax;
    58  
    59  	/* Execute the "xgetbv" instruction.  Old versions of binutils do not
    60  	 * recognize this instruction, so list the raw bytes instead.  */
    61  	__asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index));
    62  
    63  	return ((u64)edx << 32) | eax;
    64  }
    65  
    66  #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
    67  
    68  /* Initialize _cpu_features with bits for interesting processor features. */
    69  void setup_cpu_features(void)
    70  {
    71  	u32 features = 0;
    72  	u32 dummy1, dummy2, dummy3, dummy4;
    73  	u32 max_function;
    74  	u32 features_1, features_2, features_3, features_4;
    75  	bool os_saves_ymm_regs = false;
    76  
    77  	/* Get maximum supported function  */
    78  	cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
    79  	if (max_function < 1)
    80  		goto out;
    81  
    82  	/* Standard feature flags  */
    83  	cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
    84  
    85  	if (IS_SET(features_1, 26))
    86  		features |= X86_CPU_FEATURE_SSE2;
    87  
    88  	if (IS_SET(features_2, 1))
    89  		features |= X86_CPU_FEATURE_PCLMULQDQ;
    90  
    91  	if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
    92  		if ((read_xcr(0) & 0x6) == 0x6)
    93  			os_saves_ymm_regs = true;
    94  
    95  	if (os_saves_ymm_regs && IS_SET(features_2, 28))
    96  		features |= X86_CPU_FEATURE_AVX;
    97  
    98  	if (max_function < 7)
    99  		goto out;
   100  
   101  	/* Extended feature flags  */
   102  	cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
   103  
   104  	if (os_saves_ymm_regs && IS_SET(features_3, 5))
   105  		features |= X86_CPU_FEATURE_AVX2;
   106  
   107  	if (IS_SET(features_3, 8))
   108  		features |= X86_CPU_FEATURE_BMI2;
   109  
   110  out:
   111  	_cpu_features = features | X86_CPU_FEATURES_KNOWN;
   112  }
   113  
   114  #endif /* X86_CPU_FEATURES_ENABLED */