/src/php-src/Zend/zend_cpuinfo.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 2.00 of the Zend license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | http://www.zend.com/license/2_00.txt. | |
11 | | | If you did not receive a copy of the Zend license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@zend.com so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Xinchen Hui <xinchen.h@zend.com> | |
16 | | +----------------------------------------------------------------------+ |
17 | | */ |
18 | | |
19 | | #include "zend_cpuinfo.h" |
20 | | |
21 | | typedef struct _zend_cpu_info { |
22 | | uint32_t eax; |
23 | | uint32_t ebx; |
24 | | uint32_t ecx; |
25 | | uint32_t edx; |
26 | | uint32_t initialized; |
27 | | } zend_cpu_info; |
28 | | |
29 | | static zend_cpu_info cpuinfo = {0}; |
30 | | |
31 | | #if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) |
32 | | # if defined(HAVE_CPUID_H) && defined(HAVE_CPUID_COUNT) /* use cpuid.h functions */ |
33 | | # include <cpuid.h> |
34 | 48 | static void __zend_cpuid(uint32_t func, uint32_t subfunc, zend_cpu_info *cpuinfo) { |
35 | 48 | __cpuid_count(func, subfunc, cpuinfo->eax, cpuinfo->ebx, cpuinfo->ecx, cpuinfo->edx); |
36 | 48 | } |
37 | | # else /* use inline asm */ |
38 | | static void __zend_cpuid(uint32_t func, uint32_t subfunc, zend_cpu_info *cpuinfo) { |
39 | | # if defined(__i386__) && (defined(__pic__) || defined(__PIC__)) |
40 | | /* PIC on i386 uses %ebx, so preserve it. */ |
41 | | __asm__ __volatile__ ( |
42 | | "pushl %%ebx\n" |
43 | | "cpuid\n" |
44 | | "mov %%ebx,%1\n" |
45 | | "popl %%ebx" |
46 | | : "=a"(cpuinfo->eax), "=r"(cpuinfo->ebx), "=c"(cpuinfo->ecx), "=d"(cpuinfo->edx) |
47 | | : "a"(func), "c"(subfunc) |
48 | | ); |
49 | | # else |
50 | | __asm__ __volatile__ ( |
51 | | "cpuid" |
52 | | : "=a"(cpuinfo->eax), "=b"(cpuinfo->ebx), "=c"(cpuinfo->ecx), "=d"(cpuinfo->edx) |
53 | | : "a"(func), "c"(subfunc) |
54 | | ); |
55 | | # endif |
56 | | } |
57 | | # endif |
58 | | #elif defined(_MSC_VER) && !defined(__clang__) && (defined(_M_X64) || defined(_M_IX86)) /* use MSVC __cpuidex intrin */ |
59 | | # include <intrin.h> |
60 | | static void __zend_cpuid(uint32_t func, uint32_t subfunc, zend_cpu_info *cpuinfo) { |
61 | | int regs[4]; |
62 | | |
63 | | __cpuidex(regs, func, subfunc); |
64 | | |
65 | | cpuinfo->eax = regs[0]; |
66 | | cpuinfo->ebx = regs[1]; |
67 | | cpuinfo->ecx = regs[2]; |
68 | | cpuinfo->edx = regs[3]; |
69 | | } |
70 | | #else /* fall back to zero */ |
71 | | static void __zend_cpuid(uint32_t func, uint32_t subfunc, zend_cpu_info *cpuinfo) { |
72 | | cpuinfo->eax = 0; |
73 | | } |
74 | | #endif |
75 | | |
76 | | #if defined(__i386__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_IX86) |
77 | | /* Function based on compiler-rt implementation. */ |
78 | 16 | static unsigned get_xcr0_eax(void) { |
79 | 16 | # if defined(__GNUC__) || defined(__clang__) |
80 | | // Check xgetbv; this uses a .byte sequence instead of the instruction |
81 | | // directly because older assemblers do not include support for xgetbv and |
82 | | // there is no easy way to conditionally compile based on the assembler used. |
83 | 16 | unsigned eax, edx; |
84 | 16 | __asm__(".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0)); |
85 | 16 | return eax; |
86 | | # elif defined(ZEND_WIN32) && defined(_XCR_XFEATURE_ENABLED_MASK) |
87 | | return _xgetbv(_XCR_XFEATURE_ENABLED_MASK); |
88 | | # else |
89 | | return 0; |
90 | | # endif |
91 | 16 | } |
92 | | |
93 | 16 | static bool is_avx_supported(void) { |
94 | 16 | if (!(cpuinfo.ecx & ZEND_CPU_FEATURE_AVX)) { |
95 | | /* No support for AVX */ |
96 | 0 | return 0; |
97 | 0 | } |
98 | 16 | if (!(cpuinfo.ecx & ZEND_CPU_FEATURE_OSXSAVE)) { |
99 | | /* The operating system does not support XSAVE. */ |
100 | 0 | return 0; |
101 | 0 | } |
102 | 16 | if ((get_xcr0_eax() & 0x6) != 0x6) { |
103 | | /* XCR0 SSE and AVX bits must be set. */ |
104 | 0 | return 0; |
105 | 0 | } |
106 | 16 | return 1; |
107 | 16 | } |
108 | | #else |
109 | | static bool is_avx_supported(void) { |
110 | | return 0; |
111 | | } |
112 | | #endif |
113 | | |
114 | | void zend_cpu_startup(void) |
115 | 16 | { |
116 | 16 | if (!cpuinfo.initialized) { |
117 | 16 | zend_cpu_info ebx; |
118 | 16 | int max_feature; |
119 | | |
120 | 16 | cpuinfo.initialized = 1; |
121 | 16 | __zend_cpuid(0, 0, &cpuinfo); |
122 | 16 | max_feature = cpuinfo.eax; |
123 | 16 | if (max_feature == 0) { |
124 | 0 | return; |
125 | 0 | } |
126 | | |
127 | 16 | __zend_cpuid(1, 0, &cpuinfo); |
128 | | |
129 | | /* for avx2 */ |
130 | 16 | if (max_feature >= 7) { |
131 | 16 | __zend_cpuid(7, 0, &ebx); |
132 | 16 | cpuinfo.ebx = ebx.ebx; |
133 | 16 | } else { |
134 | 0 | cpuinfo.ebx = 0; |
135 | 0 | } |
136 | | |
137 | 16 | if (!is_avx_supported()) { |
138 | 0 | cpuinfo.edx &= ~ZEND_CPU_FEATURE_AVX; |
139 | 0 | cpuinfo.ebx &= ~(ZEND_CPU_FEATURE_AVX2 & ~ZEND_CPU_EBX_MASK); |
140 | 0 | } |
141 | 16 | } |
142 | 16 | } |
143 | | |
144 | 86.5k | ZEND_API int zend_cpu_supports(zend_cpu_feature feature) { |
145 | 86.5k | ZEND_ASSERT(cpuinfo.initialized); |
146 | 86.5k | if (feature & ZEND_CPU_EDX_MASK) { |
147 | 0 | return (cpuinfo.edx & (feature & ~ZEND_CPU_EDX_MASK)); |
148 | 86.5k | } else if (feature & ZEND_CPU_EBX_MASK) { |
149 | 43.2k | return (cpuinfo.ebx & (feature & ~ZEND_CPU_EBX_MASK)); |
150 | 43.2k | } else { |
151 | 43.2k | return (cpuinfo.ecx & feature); |
152 | 43.2k | } |
153 | 86.5k | } |