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