Add support for runtime x86 CPU feature detection
[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 #include "wimlib/x86_cpu_features.h"
14
15 #if defined(__i386__) || defined(__x86_64__)
16
17 #define DEBUG 0
18
19 #if DEBUG
20 #  include <stdio.h>
21 #endif
22
23 u32 _x86_cpu_features = 0;
24
25 /* Execute the CPUID instruction.  */
26 static inline void
27 cpuid(u32 leaf, u32 subleaf, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
28 {
29         __asm__ ("cpuid"
30                  : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
31                  : "a" (leaf), "c" (subleaf));
32 }
33
34 /* Read an extended control register.  */
35 static inline u64
36 read_xcr(u32 index)
37 {
38         u32 edx, eax;
39
40         __asm__ ("xgetbv" : "=d" (edx), "=a" (eax) : "c" (index));
41
42         return ((u64)edx << 32) | eax;
43 }
44
45 #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
46
47 /* Initialize _x86_cpu_features with bits for interesting processor features. */
48 void
49 x86_setup_cpu_features(void)
50 {
51         u32 features = 0;
52         u32 dummy1, dummy2, dummy3, dummy4;
53         u32 max_function;
54         u32 features_1, features_2, features_3, features_4;
55         bool os_saves_ymm_regs = false;
56
57         /* Get maximum supported function  */
58         cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
59         if (max_function < 1)
60                 goto out;
61
62         /* Standard feature flags  */
63         cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
64
65         if (IS_SET(features_1, 25))
66                 features |= X86_CPU_FEATURE_SSE;
67
68         if (IS_SET(features_1, 26))
69                 features |= X86_CPU_FEATURE_SSE2;
70
71         if (IS_SET(features_2, 0))
72                 features |= X86_CPU_FEATURE_SSE3;
73
74         if (IS_SET(features_2, 9))
75                 features |= X86_CPU_FEATURE_SSSE3;
76
77         if (IS_SET(features_2, 19))
78                 features |= X86_CPU_FEATURE_SSE4_1;
79
80         if (IS_SET(features_2, 20))
81                 features |= X86_CPU_FEATURE_SSE4_2;
82
83         if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
84                 if ((read_xcr(0) & 0x6) == 0x6)
85                         os_saves_ymm_regs = true;
86
87         if (os_saves_ymm_regs && IS_SET(features_2, 28))
88                 features |= X86_CPU_FEATURE_AVX;
89
90         if (max_function < 7)
91                 goto out;
92
93         /* Extended feature flags  */
94         cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
95
96         if (IS_SET(features_3, 3))
97                 features |= X86_CPU_FEATURE_BMI;
98
99         if (os_saves_ymm_regs && IS_SET(features_3, 5))
100                 features |= X86_CPU_FEATURE_AVX2;
101
102         if (IS_SET(features_3, 8))
103                 features |= X86_CPU_FEATURE_BMI2;
104
105 out:
106
107 #if DEBUG
108         printf("Detected x86 CPU features: ");
109         if (features & X86_CPU_FEATURE_SSE)
110                 printf("SSE ");
111         if (features & X86_CPU_FEATURE_SSE2)
112                 printf("SSE2 ");
113         if (features & X86_CPU_FEATURE_SSE3)
114                 printf("SSE3 ");
115         if (features & X86_CPU_FEATURE_SSSE3)
116                 printf("SSSE3 ");
117         if (features & X86_CPU_FEATURE_SSE4_1)
118                 printf("SSE4.1 ");
119         if (features & X86_CPU_FEATURE_SSE4_2)
120                 printf("SSE4.2 ");
121         if (features & X86_CPU_FEATURE_BMI)
122                 printf("BMI ");
123         if (features & X86_CPU_FEATURE_AVX)
124                 printf("AVX ");
125         if (features & X86_CPU_FEATURE_BMI2)
126                 printf("BMI2 ");
127         if (features & X86_CPU_FEATURE_AVX2)
128                 printf("AVX2 ");
129         printf("\n");
130 #endif /* DEBUG */
131
132         _x86_cpu_features = features | X86_CPU_FEATURES_KNOWN;
133 }
134
135 #endif /* __i386__ || __x86_64__ */