Coverage Report

Created: 2026-06-02 06:39

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