Use more comprehensive public domain dedications
[wimlib] / src / x86_cpu_features.c
1 /*
2  * x86_cpu_features.c - feature detection for x86 processors
3  *
4  * The following copying information applies to this specific source code file:
5  *
6  * Written in 2015 by Eric Biggers <ebiggers3@gmail.com>
7  *
8  * To the extent possible under law, the author(s) have dedicated all copyright
9  * and related and neighboring rights to this software to the public domain
10  * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
11  * Dedication (the "CC0").
12  *
13  * This software is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
16  *
17  * You should have received a copy of the CC0 along with this software; if not
18  * see <http://creativecommons.org/publicdomain/zero/1.0/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include "wimlib/x86_cpu_features.h"
26
27 #if defined(__i386__) || defined(__x86_64__)
28
29 #define DEBUG 0
30
31 #if DEBUG
32 #  include <stdio.h>
33 #endif
34
35 u32 _x86_cpu_features = 0;
36
37 /* With old GCC versions we have to manually save and restore the x86_32 PIC
38  * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602  */
39 #if defined(__i386__) && defined(__PIC__)
40 #  define EBX_CONSTRAINT "=r"
41 #else
42 #  define EBX_CONSTRAINT "=b"
43 #endif
44
45 /* Execute the CPUID instruction.  */
46 static inline void
47 cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
48 {
49         __asm__(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
50                 "cpuid                                  \n"
51                 ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
52                 : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
53                 : "a" (leaf), "c" (subleaf));
54 }
55
56 /* Read an extended control register.  */
57 static inline u64
58 read_xcr(u32 index)
59 {
60         u32 edx, eax;
61
62         /* Execute the "xgetbv" instruction.  Old versions of binutils do not
63          * recognize this instruction, so list the raw bytes instead.  */
64         __asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index));
65
66         return ((u64)edx << 32) | eax;
67 }
68
69 #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
70
71 /* Initialize _x86_cpu_features with bits for interesting processor features. */
72 void
73 x86_setup_cpu_features(void)
74 {
75         u32 features = 0;
76         u32 dummy1, dummy2, dummy3, dummy4;
77         u32 max_function;
78         u32 features_1, features_2, features_3, features_4;
79         bool os_saves_ymm_regs = false;
80
81         /* Get maximum supported function  */
82         cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
83         if (max_function < 1)
84                 goto out;
85
86         /* Standard feature flags  */
87         cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
88
89         if (IS_SET(features_1, 25))
90                 features |= X86_CPU_FEATURE_SSE;
91
92         if (IS_SET(features_1, 26))
93                 features |= X86_CPU_FEATURE_SSE2;
94
95         if (IS_SET(features_2, 0))
96                 features |= X86_CPU_FEATURE_SSE3;
97
98         if (IS_SET(features_2, 9))
99                 features |= X86_CPU_FEATURE_SSSE3;
100
101         if (IS_SET(features_2, 19))
102                 features |= X86_CPU_FEATURE_SSE4_1;
103
104         if (IS_SET(features_2, 20))
105                 features |= X86_CPU_FEATURE_SSE4_2;
106
107         if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
108                 if ((read_xcr(0) & 0x6) == 0x6)
109                         os_saves_ymm_regs = true;
110
111         if (os_saves_ymm_regs && IS_SET(features_2, 28))
112                 features |= X86_CPU_FEATURE_AVX;
113
114         if (max_function < 7)
115                 goto out;
116
117         /* Extended feature flags  */
118         cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
119
120         if (IS_SET(features_3, 3))
121                 features |= X86_CPU_FEATURE_BMI;
122
123         if (os_saves_ymm_regs && IS_SET(features_3, 5))
124                 features |= X86_CPU_FEATURE_AVX2;
125
126         if (IS_SET(features_3, 8))
127                 features |= X86_CPU_FEATURE_BMI2;
128
129 out:
130
131 #if DEBUG
132         printf("Detected x86 CPU features: ");
133         if (features & X86_CPU_FEATURE_SSE)
134                 printf("SSE ");
135         if (features & X86_CPU_FEATURE_SSE2)
136                 printf("SSE2 ");
137         if (features & X86_CPU_FEATURE_SSE3)
138                 printf("SSE3 ");
139         if (features & X86_CPU_FEATURE_SSSE3)
140                 printf("SSSE3 ");
141         if (features & X86_CPU_FEATURE_SSE4_1)
142                 printf("SSE4.1 ");
143         if (features & X86_CPU_FEATURE_SSE4_2)
144                 printf("SSE4.2 ");
145         if (features & X86_CPU_FEATURE_BMI)
146                 printf("BMI ");
147         if (features & X86_CPU_FEATURE_AVX)
148                 printf("AVX ");
149         if (features & X86_CPU_FEATURE_BMI2)
150                 printf("BMI2 ");
151         if (features & X86_CPU_FEATURE_AVX2)
152                 printf("AVX2 ");
153         printf("\n");
154 #endif /* DEBUG */
155
156         _x86_cpu_features = features | X86_CPU_FEATURES_KNOWN;
157 }
158
159 #endif /* __i386__ || __x86_64__ */