FFmpeg
cpu.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2025, Niklas Haas
3  * Copyright © 2018, VideoLAN and dav1d authors
4  * Copyright © 2018, Two Orioles, LLC
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <stdint.h>
30 
31 #include "checkasm_config.h"
32 #include "cpu.h"
33 #include "internal.h"
34 
35 #if ARCH_X86
36 
37 int checkasm_check_vzeroupper = 0;
38 
39 COLD int checkasm_get_check_vzeroupper(void)
40 {
41  return checkasm_check_vzeroupper;
42 }
43 
44 void checkasm_warmup_avx(void);
45 void checkasm_warmup_avx512(void);
46 void checkasm_dirty_ymm_state(void);
47 
48 static void noop(void)
49 {
50 }
51 
52 static size_t get_model_name(char *name)
53 {
54  CpuidRegisters r;
55 
56  checkasm_cpu_cpuid(&r, 0x80000000, 0);
57  if (r.eax >= 0x80000004) {
58  /* processor brand string */
59  CpuidRegisters *buf = (CpuidRegisters *) name;
60  checkasm_cpu_cpuid(buf + 0, 0x80000002, 0);
61  checkasm_cpu_cpuid(buf + 1, 0x80000003, 0);
62  checkasm_cpu_cpuid(buf + 2, 0x80000004, 0);
63  } else {
64  /* use manufacturer id as a fallback */
65  checkasm_cpu_cpuid(&r, 0, 0);
66  memcpy(name + 0, &r.ebx, 4);
67  memcpy(name + 4, &r.edx, 4);
68  memcpy(name + 8, &r.ecx, 4);
69  name[12] = '\0';
70  }
71 
72  /* trim trailing whitespace */
73  size_t len = strlen(name);
74  while (len && name[len - 1] == ' ')
75  len--;
76  name[len] = '\0';
77  return len;
78 }
79 
80 static unsigned get_cpuid(void)
81 {
82  CpuidRegisters r;
83 
84  checkasm_cpu_cpuid(&r, 0, 0);
85  const uint32_t max_leaf = r.eax;
86  if (!max_leaf)
87  return 0;
88 
89  checkasm_cpu_cpuid(&r, 1, 0);
90  const uint32_t cpuid_sig = r.eax;
91  return cpuid_sig;
92 }
93 
94 COLD char *checkasm_get_x86_cpuid(char *buf, size_t buflen)
95 {
96  if (buflen < 64)
97  return NULL;
98 
99  const size_t len = get_model_name(buf);
100  const unsigned cpuid = get_cpuid();
101  if (cpuid)
102  snprintf(buf + len, buflen - len, " (%08X)", cpuid);
103  return buf;
104 }
105 
106 COLD void checkasm_init_x86(void)
107 {
108  CpuidRegisters r;
109 
110  checkasm_cpu_cpuid(&r, 0, 0);
111  const uint32_t max_leaf = r.eax;
112  if (max_leaf < 13)
113  return;
114 
115  checkasm_cpu_cpuid(&r, 1, 0);
116  if (~r.ecx & 0x18000000 /* OSXSAVE/AVX */)
117  return;
118 
119  checkasm_cpu_cpuid(&r, 13, 1);
120  if (!(r.eax & 0x04)) /* XCR1 not supported */
121  return;
122 
123  /* Check that the state is clean after touching XMM registers (with a
124  * non-VEX-encoded instruction), without vzeroupper, after using YMM
125  * with vzeroupper. (This currently fails on Zen 4 CPUs.) */
126  checkasm_dirty_ymm_state();
127  const uint64_t xcr1 = checkasm_cpu_xgetbv(1);
128  if (xcr1 & 0x04) /* always-dirty ymm state */
129  return;
130 
131 #if ARCH_X86_32 && defined(_WIN32)
132  /* x86_32 processes on Windows can spuriously get the dirty ymm bit set
133  * while running; skip checking this aspect. */
134 #elif defined(_MSC_VER) && !defined(__clang__)
135  /* MSVC can spontaneously generate code that uses AVX registers, without
136  * properly clearing the state afterwards with vzeroupper, see
137  * https://developercommunity.visualstudio.com/t/MSVC-x64-autovectorized-AVX-code-omits-f/11102637.
138  * Skip the vzeroupper tests in this configuration. This is technically
139  * inaccurate - checkasm as a library may be built with a different
140  * toolchain than the user code under test - but it is a first rough
141  * approximation. */
142 #else
143  checkasm_check_vzeroupper = 1;
144 #endif
145 }
146 
147 typedef void (*checkasm_simd_warmup_func)(void);
148 static COLD checkasm_simd_warmup_func get_simd_warmup(void)
149 {
150  checkasm_simd_warmup_func simd_warmup = noop;
151  CpuidRegisters r;
152  checkasm_cpu_cpuid(&r, 0, 0);
153  const uint32_t max_leaf = r.eax;
154  if (max_leaf < 1)
155  return simd_warmup;
156 
157  checkasm_cpu_cpuid(&r, 1, 0);
158  if (~r.ecx & 0x18000000) /* OSXSAVE/AVX */
159  return simd_warmup;
160 
161  const uint64_t xcr0 = checkasm_cpu_xgetbv(0);
162  if (~xcr0 & 0x6) /* XMM/YMM */
163  return simd_warmup;
164 
165  simd_warmup = checkasm_warmup_avx;
166  if (max_leaf < 7 || ~xcr0 & 0xe0) /* ZMM/OPMASK */
167  return simd_warmup;
168 
169  checkasm_cpu_cpuid(&r, 7, 0);
170  if (r.ebx & 0x00000020) /* AVX512F */
171  simd_warmup = checkasm_warmup_avx512;
172 
173  return simd_warmup;
174 }
175 
176 void checkasm_simd_warmup(void)
177 {
178  static checkasm_simd_warmup_func simd_warmup = NULL;
179  if (!simd_warmup)
180  simd_warmup = get_simd_warmup();
181 
182  simd_warmup();
183 }
184 
185 #endif
COLD
#define COLD
Definition: internal.h:45
r
const char * r
Definition: vf_curves.c:127
checkasm_config.h
name
const char * name
Definition: cpu.c:41
NULL
#define NULL
Definition: coverity.c:32
noop
#define noop(a)
Definition: h264chroma_template.c:71
len
int len
Definition: vorbis_enc_data.h:426
snprintf
#define snprintf
Definition: snprintf.h:34