786dce2b1b43c9264ff2f310ed77f119aea2733c
[wimlib] / src / x86_cpu_features.c
1 /*
2  * x86_cpu_features.c
3  *
4  * Feature detection for x86 processors.
5  *
6  * Author:      Eric Biggers
7  * Year:        2015
8  *
9  * The author dedicates this file to the public domain.
10  * You can do whatever you want with this file.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 #  include "config.h"
15 #endif
16
17 #include "wimlib/x86_cpu_features.h"
18
19 #if defined(__i386__) || defined(__x86_64__)
20
21 #define DEBUG 0
22
23 #if DEBUG
24 #  include <stdio.h>
25 #endif
26
27 u32 _x86_cpu_features = 0;
28
29 /* With old GCC versions we have to manually save and restore the x86_32 PIC
30  * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602  */
31 #if defined(__i386__) && defined(__PIC__)
32 #  define EBX_CONSTRAINT "=r"
33 #else
34 #  define EBX_CONSTRAINT "=b"
35 #endif
36
37 /* Execute the CPUID instruction.  */
38 static inline void
39 cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
40 {
41         __asm__(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
42                 "cpuid                                  \n"
43                 ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
44                 : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
45                 : "a" (leaf), "c" (subleaf));
46 }
47
48 /* Read an extended control register.  */
49 static inline u64
50 read_xcr(u32 index)
51 {
52         u32 edx, eax;
53
54         /* Execute the "xgetbv" instruction.  Old versions of binutils do not
55          * recognize this instruction, so list the raw bytes instead.  */
56         __asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index));
57
58         return ((u64)edx << 32) | eax;
59 }
60
61 #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
62
63 /* Initialize _x86_cpu_features with bits for interesting processor features. */
64 void
65 x86_setup_cpu_features(void)
66 {
67         u32 features = 0;
68         u32 dummy1, dummy2, dummy3, dummy4;
69         u32 max_function;
70         u32 features_1, features_2, features_3, features_4;
71         bool os_saves_ymm_regs = false;
72
73         /* Get maximum supported function  */
74         cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
75         if (max_function < 1)
76                 goto out;
77
78         /* Standard feature flags  */
79         cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
80
81         if (IS_SET(features_1, 25))
82                 features |= X86_CPU_FEATURE_SSE;
83
84         if (IS_SET(features_1, 26))
85                 features |= X86_CPU_FEATURE_SSE2;
86
87         if (IS_SET(features_2, 0))
88                 features |= X86_CPU_FEATURE_SSE3;
89
90         if (IS_SET(features_2, 9))
91                 features |= X86_CPU_FEATURE_SSSE3;
92
93         if (IS_SET(features_2, 19))
94                 features |= X86_CPU_FEATURE_SSE4_1;
95
96         if (IS_SET(features_2, 20))
97                 features |= X86_CPU_FEATURE_SSE4_2;
98
99         if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
100                 if ((read_xcr(0) & 0x6) == 0x6)
101                         os_saves_ymm_regs = true;
102
103         if (os_saves_ymm_regs && IS_SET(features_2, 28))
104                 features |= X86_CPU_FEATURE_AVX;
105
106         if (max_function < 7)
107                 goto out;
108
109         /* Extended feature flags  */
110         cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
111
112         if (IS_SET(features_3, 3))
113                 features |= X86_CPU_FEATURE_BMI;
114
115         if (os_saves_ymm_regs && IS_SET(features_3, 5))
116                 features |= X86_CPU_FEATURE_AVX2;
117
118         if (IS_SET(features_3, 8))
119                 features |= X86_CPU_FEATURE_BMI2;
120
121 out:
122
123 #if DEBUG
124         printf("Detected x86 CPU features: ");
125         if (features & X86_CPU_FEATURE_SSE)
126                 printf("SSE ");
127         if (features & X86_CPU_FEATURE_SSE2)
128                 printf("SSE2 ");
129         if (features & X86_CPU_FEATURE_SSE3)
130                 printf("SSE3 ");
131         if (features & X86_CPU_FEATURE_SSSE3)
132                 printf("SSSE3 ");
133         if (features & X86_CPU_FEATURE_SSE4_1)
134                 printf("SSE4.1 ");
135         if (features & X86_CPU_FEATURE_SSE4_2)
136                 printf("SSE4.2 ");
137         if (features & X86_CPU_FEATURE_BMI)
138                 printf("BMI ");
139         if (features & X86_CPU_FEATURE_AVX)
140                 printf("AVX ");
141         if (features & X86_CPU_FEATURE_BMI2)
142                 printf("BMI2 ");
143         if (features & X86_CPU_FEATURE_AVX2)
144                 printf("AVX2 ");
145         printf("\n");
146 #endif /* DEBUG */
147
148         _x86_cpu_features = features | X86_CPU_FEATURES_KNOWN;
149 }
150
151 #endif /* __i386__ || __x86_64__ */