Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Assembly testing and benchmarking tool | ||
3 | * Copyright (c) 2015 Henrik Gramner | ||
4 | * Copyright (c) 2008 Loren Merritt | ||
5 | * | ||
6 | * This file is part of FFmpeg. | ||
7 | * | ||
8 | * FFmpeg is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * FFmpeg is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with FFmpeg; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | * | ||
22 | * Copyright © 2018, VideoLAN and dav1d authors | ||
23 | * Copyright © 2018, Two Orioles, LLC | ||
24 | * All rights reserved. | ||
25 | * | ||
26 | * Redistribution and use in source and binary forms, with or without | ||
27 | * modification, are permitted provided that the following conditions are met: | ||
28 | * | ||
29 | * 1. Redistributions of source code must retain the above copyright notice, this | ||
30 | * list of conditions and the following disclaimer. | ||
31 | * | ||
32 | * 2. Redistributions in binary form must reproduce the above copyright notice, | ||
33 | * this list of conditions and the following disclaimer in the documentation | ||
34 | * and/or other materials provided with the distribution. | ||
35 | * | ||
36 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
37 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
38 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
39 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
40 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
41 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
42 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
43 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
44 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
45 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
46 | */ | ||
47 | |||
48 | #include "config.h" | ||
49 | #include "config_components.h" | ||
50 | |||
51 | #ifndef _GNU_SOURCE | ||
52 | # define _GNU_SOURCE // for syscall (performance monitoring API), strsignal() | ||
53 | #endif | ||
54 | |||
55 | #include <signal.h> | ||
56 | #include <stdarg.h> | ||
57 | #include <stdio.h> | ||
58 | #include <stdlib.h> | ||
59 | #include <string.h> | ||
60 | #include "checkasm.h" | ||
61 | #include "libavutil/avassert.h" | ||
62 | #include "libavutil/common.h" | ||
63 | #include "libavutil/cpu.h" | ||
64 | #include "libavutil/intfloat.h" | ||
65 | #include "libavutil/random_seed.h" | ||
66 | |||
67 | #if HAVE_IO_H | ||
68 | #include <io.h> | ||
69 | #endif | ||
70 | #if HAVE_PRCTL | ||
71 | #include <sys/prctl.h> | ||
72 | #endif | ||
73 | |||
74 | #if defined(_WIN32) && !defined(SIGBUS) | ||
75 | /* non-standard, use the same value as mingw-w64 */ | ||
76 | #define SIGBUS 10 | ||
77 | #endif | ||
78 | |||
79 | #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE | ||
80 | #include <windows.h> | ||
81 | #define COLOR_RED FOREGROUND_RED | ||
82 | #define COLOR_GREEN FOREGROUND_GREEN | ||
83 | #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN) | ||
84 | #else | ||
85 | #define COLOR_RED 1 | ||
86 | #define COLOR_GREEN 2 | ||
87 | #define COLOR_YELLOW 3 | ||
88 | #endif | ||
89 | |||
90 | #if HAVE_UNISTD_H | ||
91 | #include <unistd.h> | ||
92 | #endif | ||
93 | |||
94 | #if !HAVE_ISATTY | ||
95 | #define isatty(fd) 1 | ||
96 | #endif | ||
97 | |||
98 | #if ARCH_AARCH64 | ||
99 | #include "libavutil/aarch64/cpu.h" | ||
100 | #elif ARCH_RISCV | ||
101 | #include "libavutil/riscv/cpu.h" | ||
102 | #endif | ||
103 | |||
104 | #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL | ||
105 | #include "libavutil/arm/cpu.h" | ||
106 | |||
107 | void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp; | ||
108 | #endif | ||
109 | |||
110 | /* Trade-off between speed and accuracy */ | ||
111 | uint64_t bench_runs = 1U << 10; | ||
112 | |||
113 | /* List of tests to invoke */ | ||
114 | static const struct { | ||
115 | const char *name; | ||
116 | void (*func)(void); | ||
117 | } tests[] = { | ||
118 | #if CONFIG_AVCODEC | ||
119 | #if CONFIG_AAC_DECODER | ||
120 | { "aacpsdsp", checkasm_check_aacpsdsp }, | ||
121 | { "sbrdsp", checkasm_check_sbrdsp }, | ||
122 | #endif | ||
123 | #if CONFIG_AAC_ENCODER | ||
124 | { "aacencdsp", checkasm_check_aacencdsp }, | ||
125 | #endif | ||
126 | #if CONFIG_AC3DSP | ||
127 | { "ac3dsp", checkasm_check_ac3dsp }, | ||
128 | #endif | ||
129 | #if CONFIG_ALAC_DECODER | ||
130 | { "alacdsp", checkasm_check_alacdsp }, | ||
131 | #endif | ||
132 | #if CONFIG_APV_DECODER | ||
133 | { "apv_dsp", checkasm_check_apv_dsp }, | ||
134 | #endif | ||
135 | #if CONFIG_AUDIODSP | ||
136 | { "audiodsp", checkasm_check_audiodsp }, | ||
137 | #endif | ||
138 | #if CONFIG_BLOCKDSP | ||
139 | { "blockdsp", checkasm_check_blockdsp }, | ||
140 | #endif | ||
141 | #if CONFIG_BSWAPDSP | ||
142 | { "bswapdsp", checkasm_check_bswapdsp }, | ||
143 | #endif | ||
144 | #if CONFIG_CAVS_DECODER | ||
145 | { "cavsdsp", checkasm_check_cavsdsp }, | ||
146 | #endif | ||
147 | #if CONFIG_DCA_DECODER | ||
148 | { "dcadsp", checkasm_check_dcadsp }, | ||
149 | { "synth_filter", checkasm_check_synth_filter }, | ||
150 | #endif | ||
151 | #if CONFIG_DIRAC_DECODER | ||
152 | { "diracdsp", checkasm_check_diracdsp }, | ||
153 | #endif | ||
154 | #if CONFIG_EXR_DECODER | ||
155 | { "exrdsp", checkasm_check_exrdsp }, | ||
156 | #endif | ||
157 | #if CONFIG_FDCTDSP | ||
158 | { "fdctdsp", checkasm_check_fdctdsp }, | ||
159 | #endif | ||
160 | #if CONFIG_FLAC_DECODER | ||
161 | { "flacdsp", checkasm_check_flacdsp }, | ||
162 | #endif | ||
163 | #if CONFIG_FMTCONVERT | ||
164 | { "fmtconvert", checkasm_check_fmtconvert }, | ||
165 | #endif | ||
166 | #if CONFIG_G722DSP | ||
167 | { "g722dsp", checkasm_check_g722dsp }, | ||
168 | #endif | ||
169 | #if CONFIG_H263DSP | ||
170 | { "h263dsp", checkasm_check_h263dsp }, | ||
171 | #endif | ||
172 | #if CONFIG_H264CHROMA | ||
173 | { "h264chroma", checkasm_check_h264chroma }, | ||
174 | #endif | ||
175 | #if CONFIG_H264DSP | ||
176 | { "h264dsp", checkasm_check_h264dsp }, | ||
177 | #endif | ||
178 | #if CONFIG_H264PRED | ||
179 | { "h264pred", checkasm_check_h264pred }, | ||
180 | #endif | ||
181 | #if CONFIG_H264QPEL | ||
182 | { "h264qpel", checkasm_check_h264qpel }, | ||
183 | #endif | ||
184 | #if CONFIG_HEVC_DECODER | ||
185 | { "hevc_add_res", checkasm_check_hevc_add_res }, | ||
186 | { "hevc_deblock", checkasm_check_hevc_deblock }, | ||
187 | { "hevc_idct", checkasm_check_hevc_idct }, | ||
188 | { "hevc_pel", checkasm_check_hevc_pel }, | ||
189 | { "hevc_sao", checkasm_check_hevc_sao }, | ||
190 | #endif | ||
191 | #if CONFIG_HPELDSP | ||
192 | { "hpeldsp", checkasm_check_hpeldsp }, | ||
193 | #endif | ||
194 | #if CONFIG_HUFFYUV_DECODER | ||
195 | { "huffyuvdsp", checkasm_check_huffyuvdsp }, | ||
196 | #endif | ||
197 | #if CONFIG_IDCTDSP | ||
198 | { "idctdsp", checkasm_check_idctdsp }, | ||
199 | #endif | ||
200 | #if CONFIG_JPEG2000_DECODER | ||
201 | { "jpeg2000dsp", checkasm_check_jpeg2000dsp }, | ||
202 | #endif | ||
203 | #if CONFIG_LLAUDDSP | ||
204 | { "llauddsp", checkasm_check_llauddsp }, | ||
205 | #endif | ||
206 | #if CONFIG_HUFFYUVDSP | ||
207 | { "llviddsp", checkasm_check_llviddsp }, | ||
208 | #endif | ||
209 | #if CONFIG_LLVIDENCDSP | ||
210 | { "llviddspenc", checkasm_check_llviddspenc }, | ||
211 | #endif | ||
212 | #if CONFIG_LPC | ||
213 | { "lpc", checkasm_check_lpc }, | ||
214 | #endif | ||
215 | #if CONFIG_ME_CMP | ||
216 | { "motion", checkasm_check_motion }, | ||
217 | #endif | ||
218 | #if CONFIG_MPEGVIDEOENCDSP | ||
219 | { "mpegvideoencdsp", checkasm_check_mpegvideoencdsp }, | ||
220 | #endif | ||
221 | #if CONFIG_OPUS_DECODER | ||
222 | { "opusdsp", checkasm_check_opusdsp }, | ||
223 | #endif | ||
224 | #if CONFIG_PIXBLOCKDSP | ||
225 | { "pixblockdsp", checkasm_check_pixblockdsp }, | ||
226 | #endif | ||
227 | #if CONFIG_QPELDSP | ||
228 | { "qpeldsp", checkasm_check_qpeldsp }, | ||
229 | #endif | ||
230 | #if CONFIG_RV34DSP | ||
231 | { "rv34dsp", checkasm_check_rv34dsp }, | ||
232 | #endif | ||
233 | #if CONFIG_RV40_DECODER | ||
234 | { "rv40dsp", checkasm_check_rv40dsp }, | ||
235 | #endif | ||
236 | #if CONFIG_SVQ1_ENCODER | ||
237 | { "svq1enc", checkasm_check_svq1enc }, | ||
238 | #endif | ||
239 | #if CONFIG_TAK_DECODER | ||
240 | { "takdsp", checkasm_check_takdsp }, | ||
241 | #endif | ||
242 | #if CONFIG_UTVIDEO_DECODER | ||
243 | { "utvideodsp", checkasm_check_utvideodsp }, | ||
244 | #endif | ||
245 | #if CONFIG_V210_DECODER | ||
246 | { "v210dec", checkasm_check_v210dec }, | ||
247 | #endif | ||
248 | #if CONFIG_V210_ENCODER | ||
249 | { "v210enc", checkasm_check_v210enc }, | ||
250 | #endif | ||
251 | #if CONFIG_VC1DSP | ||
252 | { "vc1dsp", checkasm_check_vc1dsp }, | ||
253 | #endif | ||
254 | #if CONFIG_VP8DSP | ||
255 | { "vp8dsp", checkasm_check_vp8dsp }, | ||
256 | #endif | ||
257 | #if CONFIG_VP9_DECODER | ||
258 | { "vp9dsp", checkasm_check_vp9dsp }, | ||
259 | #endif | ||
260 | #if CONFIG_VIDEODSP | ||
261 | { "videodsp", checkasm_check_videodsp }, | ||
262 | #endif | ||
263 | #if CONFIG_VORBIS_DECODER | ||
264 | { "vorbisdsp", checkasm_check_vorbisdsp }, | ||
265 | #endif | ||
266 | #if CONFIG_VVC_DECODER | ||
267 | { "vvc_alf", checkasm_check_vvc_alf }, | ||
268 | { "vvc_mc", checkasm_check_vvc_mc }, | ||
269 | { "vvc_sao", checkasm_check_vvc_sao }, | ||
270 | #endif | ||
271 | #endif | ||
272 | #if CONFIG_AVFILTER | ||
273 | #if CONFIG_SCENE_SAD | ||
274 | { "scene_sad", checkasm_check_scene_sad }, | ||
275 | #endif | ||
276 | #if CONFIG_AFIR_FILTER | ||
277 | { "af_afir", checkasm_check_afir }, | ||
278 | #endif | ||
279 | #if CONFIG_BLACKDETECT_FILTER | ||
280 | { "vf_blackdetect", checkasm_check_blackdetect }, | ||
281 | #endif | ||
282 | #if CONFIG_BLEND_FILTER | ||
283 | { "vf_blend", checkasm_check_blend }, | ||
284 | #endif | ||
285 | #if CONFIG_BWDIF_FILTER | ||
286 | { "vf_bwdif", checkasm_check_vf_bwdif }, | ||
287 | #endif | ||
288 | #if CONFIG_COLORDETECT_FILTER | ||
289 | { "vf_colordetect", checkasm_check_colordetect }, | ||
290 | #endif | ||
291 | #if CONFIG_COLORSPACE_FILTER | ||
292 | { "vf_colorspace", checkasm_check_colorspace }, | ||
293 | #endif | ||
294 | #if CONFIG_EQ_FILTER | ||
295 | { "vf_eq", checkasm_check_vf_eq }, | ||
296 | #endif | ||
297 | #if CONFIG_GBLUR_FILTER | ||
298 | { "vf_gblur", checkasm_check_vf_gblur }, | ||
299 | #endif | ||
300 | #if CONFIG_HFLIP_FILTER | ||
301 | { "vf_hflip", checkasm_check_vf_hflip }, | ||
302 | #endif | ||
303 | #if CONFIG_IDET_FILTER | ||
304 | { "vf_idet", checkasm_check_idet }, | ||
305 | #endif | ||
306 | #if CONFIG_NLMEANS_FILTER | ||
307 | { "vf_nlmeans", checkasm_check_nlmeans }, | ||
308 | #endif | ||
309 | #if CONFIG_THRESHOLD_FILTER | ||
310 | { "vf_threshold", checkasm_check_vf_threshold }, | ||
311 | #endif | ||
312 | #if CONFIG_SOBEL_FILTER | ||
313 | { "vf_sobel", checkasm_check_vf_sobel }, | ||
314 | #endif | ||
315 | #endif | ||
316 | #if CONFIG_SWSCALE | ||
317 | { "sw_gbrp", checkasm_check_sw_gbrp }, | ||
318 | { "sw_range_convert", checkasm_check_sw_range_convert }, | ||
319 | { "sw_rgb", checkasm_check_sw_rgb }, | ||
320 | { "sw_scale", checkasm_check_sw_scale }, | ||
321 | { "sw_yuv2rgb", checkasm_check_sw_yuv2rgb }, | ||
322 | { "sw_yuv2yuv", checkasm_check_sw_yuv2yuv }, | ||
323 | { "sw_ops", checkasm_check_sw_ops }, | ||
324 | #endif | ||
325 | #if CONFIG_AVUTIL | ||
326 | { "aes", checkasm_check_aes }, | ||
327 | { "fixed_dsp", checkasm_check_fixed_dsp }, | ||
328 | { "float_dsp", checkasm_check_float_dsp }, | ||
329 | { "lls", checkasm_check_lls }, | ||
330 | { "av_tx", checkasm_check_av_tx }, | ||
331 | #endif | ||
332 | { NULL } | ||
333 | }; | ||
334 | |||
335 | /* List of cpu flags to check */ | ||
336 | static const struct { | ||
337 | const char *name; | ||
338 | const char *suffix; | ||
339 | int flag; | ||
340 | } cpus[] = { | ||
341 | #if ARCH_AARCH64 | ||
342 | { "ARMV8", "armv8", AV_CPU_FLAG_ARMV8 }, | ||
343 | { "NEON", "neon", AV_CPU_FLAG_NEON }, | ||
344 | { "DOTPROD", "dotprod", AV_CPU_FLAG_DOTPROD }, | ||
345 | { "I8MM", "i8mm", AV_CPU_FLAG_I8MM }, | ||
346 | { "SVE", "sve", AV_CPU_FLAG_SVE }, | ||
347 | { "SVE2", "sve2", AV_CPU_FLAG_SVE2 }, | ||
348 | #elif ARCH_ARM | ||
349 | { "ARMV5TE", "armv5te", AV_CPU_FLAG_ARMV5TE }, | ||
350 | { "ARMV6", "armv6", AV_CPU_FLAG_ARMV6 }, | ||
351 | { "ARMV6T2", "armv6t2", AV_CPU_FLAG_ARMV6T2 }, | ||
352 | { "VFP", "vfp", AV_CPU_FLAG_VFP }, | ||
353 | { "VFP_VM", "vfp_vm", AV_CPU_FLAG_VFP_VM }, | ||
354 | { "VFPV3", "vfp3", AV_CPU_FLAG_VFPV3 }, | ||
355 | { "NEON", "neon", AV_CPU_FLAG_NEON }, | ||
356 | #elif ARCH_PPC | ||
357 | { "ALTIVEC", "altivec", AV_CPU_FLAG_ALTIVEC }, | ||
358 | { "VSX", "vsx", AV_CPU_FLAG_VSX }, | ||
359 | { "POWER8", "power8", AV_CPU_FLAG_POWER8 }, | ||
360 | #elif ARCH_RISCV | ||
361 | { "RVI", "rvi", AV_CPU_FLAG_RVI }, | ||
362 | { "misaligned", "misaligned", AV_CPU_FLAG_RV_MISALIGNED }, | ||
363 | { "RV_zbb", "rvb_b", AV_CPU_FLAG_RVB_BASIC }, | ||
364 | { "RVB", "rvb", AV_CPU_FLAG_RVB }, | ||
365 | { "RV_zve32x","rvv_i32", AV_CPU_FLAG_RVV_I32 }, | ||
366 | { "RV_zve32f","rvv_f32", AV_CPU_FLAG_RVV_F32 }, | ||
367 | { "RV_zve64x","rvv_i64", AV_CPU_FLAG_RVV_I64 }, | ||
368 | { "RV_zve64d","rvv_f64", AV_CPU_FLAG_RVV_F64 }, | ||
369 | { "RV_zvbb", "rv_zvbb", AV_CPU_FLAG_RV_ZVBB }, | ||
370 | #elif ARCH_MIPS | ||
371 | { "MMI", "mmi", AV_CPU_FLAG_MMI }, | ||
372 | { "MSA", "msa", AV_CPU_FLAG_MSA }, | ||
373 | #elif ARCH_X86 | ||
374 | { "MMX", "mmx", AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV }, | ||
375 | { "MMXEXT", "mmxext", AV_CPU_FLAG_MMXEXT }, | ||
376 | { "3DNOW", "3dnow", AV_CPU_FLAG_3DNOW }, | ||
377 | { "3DNOWEXT", "3dnowext", AV_CPU_FLAG_3DNOWEXT }, | ||
378 | { "SSE", "sse", AV_CPU_FLAG_SSE }, | ||
379 | { "SSE2", "sse2", AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW }, | ||
380 | { "SSE3", "sse3", AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW }, | ||
381 | { "SSSE3", "ssse3", AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM }, | ||
382 | { "SSE4.1", "sse4", AV_CPU_FLAG_SSE4 }, | ||
383 | { "SSE4.2", "sse42", AV_CPU_FLAG_SSE42 }, | ||
384 | { "AES-NI", "aesni", AV_CPU_FLAG_AESNI }, | ||
385 | { "AVX", "avx", AV_CPU_FLAG_AVX }, | ||
386 | { "XOP", "xop", AV_CPU_FLAG_XOP }, | ||
387 | { "FMA3", "fma3", AV_CPU_FLAG_FMA3 }, | ||
388 | { "FMA4", "fma4", AV_CPU_FLAG_FMA4 }, | ||
389 | { "AVX2", "avx2", AV_CPU_FLAG_AVX2 }, | ||
390 | { "AVX-512", "avx512", AV_CPU_FLAG_AVX512 }, | ||
391 | { "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL }, | ||
392 | #elif ARCH_LOONGARCH | ||
393 | { "LSX", "lsx", AV_CPU_FLAG_LSX }, | ||
394 | { "LASX", "lasx", AV_CPU_FLAG_LASX }, | ||
395 | #elif ARCH_WASM | ||
396 | { "SIMD128", "simd128", AV_CPU_FLAG_SIMD128 }, | ||
397 | #endif | ||
398 | { NULL } | ||
399 | }; | ||
400 | |||
401 | typedef struct CheckasmFuncVersion { | ||
402 | struct CheckasmFuncVersion *next; | ||
403 | void *func; | ||
404 | int ok; | ||
405 | int cpu; | ||
406 | CheckasmPerf perf; | ||
407 | } CheckasmFuncVersion; | ||
408 | |||
409 | /* Binary search tree node */ | ||
410 | typedef struct CheckasmFunc { | ||
411 | struct CheckasmFunc *child[2]; | ||
412 | CheckasmFuncVersion versions; | ||
413 | uint8_t color; /* 0 = red, 1 = black */ | ||
414 | char name[1]; | ||
415 | } CheckasmFunc; | ||
416 | |||
417 | /* Internal state */ | ||
418 | static struct { | ||
419 | CheckasmFunc *funcs; | ||
420 | CheckasmFunc *current_func; | ||
421 | CheckasmFuncVersion *current_func_ver; | ||
422 | const char *current_test_name; | ||
423 | const char *bench_pattern; | ||
424 | int bench_pattern_len; | ||
425 | int num_checked; | ||
426 | int num_failed; | ||
427 | |||
428 | /* perf */ | ||
429 | int nop_time; | ||
430 | int sysfd; | ||
431 | |||
432 | int cpu_flag; | ||
433 | const char *cpu_flag_name; | ||
434 | const char *test_pattern; | ||
435 | int verbose; | ||
436 | int csv; | ||
437 | int tsv; | ||
438 | volatile sig_atomic_t catch_signals; | ||
439 | } state; | ||
440 | |||
441 | /* PRNG state */ | ||
442 | AVLFG checkasm_lfg; | ||
443 | |||
444 | /* float compare support code */ | ||
445 | 12610184 | static int is_negative(union av_intfloat32 u) | |
446 | { | ||
447 | 12610184 | return u.i >> 31; | |
448 | } | ||
449 | |||
450 | 6305092 | int float_near_ulp(float a, float b, unsigned max_ulp) | |
451 | { | ||
452 | union av_intfloat32 x, y; | ||
453 | |||
454 | 6305092 | x.f = a; | |
455 | 6305092 | y.f = b; | |
456 | |||
457 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6305092 times.
|
6305092 | if (is_negative(x) != is_negative(y)) { |
458 | // handle -0.0 == +0.0 | ||
459 | ✗ | return a == b; | |
460 | } | ||
461 | |||
462 |
2/2✓ Branch 0 taken 6305085 times.
✓ Branch 1 taken 7 times.
|
6305092 | if (llabs((int64_t)x.i - y.i) <= max_ulp) |
463 | 6305085 | return 1; | |
464 | |||
465 | 7 | return 0; | |
466 | } | ||
467 | |||
468 | 10983 | int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp, | |
469 | unsigned len) | ||
470 | { | ||
471 | unsigned i; | ||
472 | |||
473 |
2/2✓ Branch 0 taken 6299972 times.
✓ Branch 1 taken 10983 times.
|
6310955 | for (i = 0; i < len; i++) { |
474 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6299972 times.
|
6299972 | if (!float_near_ulp(a[i], b[i], max_ulp)) |
475 | ✗ | return 0; | |
476 | } | ||
477 | 10983 | return 1; | |
478 | } | ||
479 | |||
480 | 910812 | int float_near_abs_eps(float a, float b, float eps) | |
481 | { | ||
482 | 910812 | float abs_diff = fabsf(a - b); | |
483 |
1/2✓ Branch 0 taken 910812 times.
✗ Branch 1 not taken.
|
910812 | if (abs_diff < eps) |
484 | 910812 | return 1; | |
485 | |||
486 | ✗ | fprintf(stderr, "test failed comparing %g with %g (abs diff=%g with EPS=%g)\n", a, b, abs_diff, eps); | |
487 | |||
488 | ✗ | return 0; | |
489 | } | ||
490 | |||
491 | 196 | int float_near_abs_eps_array(const float *a, const float *b, float eps, | |
492 | unsigned len) | ||
493 | { | ||
494 | unsigned i; | ||
495 | |||
496 |
2/2✓ Branch 0 taken 900810 times.
✓ Branch 1 taken 196 times.
|
901006 | for (i = 0; i < len; i++) { |
497 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 900810 times.
|
900810 | if (!float_near_abs_eps(a[i], b[i], eps)) |
498 | ✗ | return 0; | |
499 | } | ||
500 | 196 | return 1; | |
501 | } | ||
502 | |||
503 | 5120 | int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp) | |
504 | { | ||
505 |
3/4✓ Branch 1 taken 7 times.
✓ Branch 2 taken 5113 times.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
5120 | return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps); |
506 | } | ||
507 | |||
508 | ✗ | int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps, | |
509 | unsigned max_ulp, unsigned len) | ||
510 | { | ||
511 | unsigned i; | ||
512 | |||
513 | ✗ | for (i = 0; i < len; i++) { | |
514 | ✗ | if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp)) | |
515 | ✗ | return 0; | |
516 | } | ||
517 | ✗ | return 1; | |
518 | } | ||
519 | |||
520 | 69931 | int double_near_abs_eps(double a, double b, double eps) | |
521 | { | ||
522 | 69931 | double abs_diff = fabs(a - b); | |
523 | |||
524 | 69931 | return abs_diff < eps; | |
525 | } | ||
526 | |||
527 | 11 | int double_near_abs_eps_array(const double *a, const double *b, double eps, | |
528 | unsigned len) | ||
529 | { | ||
530 | unsigned i; | ||
531 | |||
532 |
2/2✓ Branch 0 taken 41068 times.
✓ Branch 1 taken 11 times.
|
41079 | for (i = 0; i < len; i++) { |
533 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 41068 times.
|
41068 | if (!double_near_abs_eps(a[i], b[i], eps)) |
534 | ✗ | return 0; | |
535 | } | ||
536 | 11 | return 1; | |
537 | } | ||
538 | |||
539 | /* Print colored text to stderr if the terminal supports it */ | ||
540 | 640 | static void color_printf(int color, const char *fmt, ...) | |
541 | { | ||
542 | static int use_color = -1; | ||
543 | va_list arg; | ||
544 | |||
545 | #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE | ||
546 | static HANDLE con; | ||
547 | static WORD org_attributes; | ||
548 | |||
549 | if (use_color < 0) { | ||
550 | CONSOLE_SCREEN_BUFFER_INFO con_info; | ||
551 | con = GetStdHandle(STD_ERROR_HANDLE); | ||
552 | if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) { | ||
553 | org_attributes = con_info.wAttributes; | ||
554 | use_color = 1; | ||
555 | } else | ||
556 | use_color = 0; | ||
557 | } | ||
558 | if (use_color) | ||
559 | SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f)); | ||
560 | #else | ||
561 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 566 times.
|
640 | if (use_color < 0) { |
562 | 74 | const char *term = getenv("TERM"); | |
563 |
3/6✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 74 times.
|
74 | use_color = term && strcmp(term, "dumb") && isatty(2); |
564 | } | ||
565 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 640 times.
|
640 | if (use_color) |
566 | ✗ | fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07); | |
567 | #endif | ||
568 | |||
569 | 640 | va_start(arg, fmt); | |
570 | 640 | vfprintf(stderr, fmt, arg); | |
571 | 640 | va_end(arg); | |
572 | |||
573 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 640 times.
|
640 | if (use_color) { |
574 | #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE | ||
575 | SetConsoleTextAttribute(con, org_attributes); | ||
576 | #else | ||
577 | ✗ | fprintf(stderr, "\x1b[0m"); | |
578 | #endif | ||
579 | } | ||
580 | 640 | } | |
581 | |||
582 | /* Deallocate a tree */ | ||
583 | 25298 | static void destroy_func_tree(CheckasmFunc *f) | |
584 | { | ||
585 |
2/2✓ Branch 0 taken 12609 times.
✓ Branch 1 taken 12689 times.
|
25298 | if (f) { |
586 | 12609 | CheckasmFuncVersion *v = f->versions.next; | |
587 |
2/2✓ Branch 0 taken 14865 times.
✓ Branch 1 taken 12609 times.
|
27474 | while (v) { |
588 | 14865 | CheckasmFuncVersion *next = v->next; | |
589 | 14865 | free(v); | |
590 | 14865 | v = next; | |
591 | } | ||
592 | |||
593 | 12609 | destroy_func_tree(f->child[0]); | |
594 | 12609 | destroy_func_tree(f->child[1]); | |
595 | 12609 | free(f); | |
596 | } | ||
597 | 25298 | } | |
598 | |||
599 | /* Allocate a zero-initialized block, clean up and exit on failure */ | ||
600 | 27474 | static void *checkasm_malloc(size_t size) | |
601 | { | ||
602 | 27474 | void *ptr = calloc(1, size); | |
603 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27474 times.
|
27474 | if (!ptr) { |
604 | ✗ | fprintf(stderr, "checkasm: malloc failed\n"); | |
605 | ✗ | destroy_func_tree(state.funcs); | |
606 | ✗ | exit(1); | |
607 | } | ||
608 | 27474 | return ptr; | |
609 | } | ||
610 | |||
611 | /* Get the suffix of the specified cpu flag */ | ||
612 | ✗ | static const char *cpu_suffix(int cpu) | |
613 | { | ||
614 | ✗ | int i = FF_ARRAY_ELEMS(cpus); | |
615 | |||
616 | ✗ | while (--i >= 0) | |
617 | ✗ | if (cpu & cpus[i].flag) | |
618 | ✗ | return cpus[i].suffix; | |
619 | |||
620 | ✗ | return "c"; | |
621 | } | ||
622 | |||
623 | ✗ | static int cmp_nop(const void *a, const void *b) | |
624 | { | ||
625 | ✗ | return *(const uint16_t*)a - *(const uint16_t*)b; | |
626 | } | ||
627 | |||
628 | /* Measure the overhead of the timing code (in decicycles) */ | ||
629 | ✗ | static int measure_nop_time(void) | |
630 | { | ||
631 | uint16_t nops[10000]; | ||
632 | ✗ | int i, nop_sum = 0; | |
633 | ✗ | av_unused const int sysfd = state.sysfd; | |
634 | |||
635 | ✗ | uint64_t t = 0; | |
636 | ✗ | for (i = 0; i < 10000; i++) { | |
637 | ✗ | PERF_START(t); | |
638 | ✗ | PERF_STOP(t); | |
639 | ✗ | nops[i] = t; | |
640 | } | ||
641 | |||
642 | ✗ | qsort(nops, 10000, sizeof(uint16_t), cmp_nop); | |
643 | ✗ | for (i = 2500; i < 7500; i++) | |
644 | ✗ | nop_sum += nops[i]; | |
645 | |||
646 | ✗ | return nop_sum / 500; | |
647 | } | ||
648 | |||
649 | ✗ | static inline double avg_cycles_per_call(const CheckasmPerf *const p) | |
650 | { | ||
651 | ✗ | if (p->iterations) { | |
652 | ✗ | const double cycles = (double)(10 * p->cycles) / p->iterations - state.nop_time; | |
653 | ✗ | if (cycles > 0.0) | |
654 | ✗ | return cycles / 32.0; /* 32 calls per iteration */ | |
655 | } | ||
656 | ✗ | return 0.0; | |
657 | } | ||
658 | |||
659 | /* Print benchmark results */ | ||
660 | ✗ | static void print_benchs(CheckasmFunc *f) | |
661 | { | ||
662 | ✗ | if (f) { | |
663 | ✗ | CheckasmFuncVersion *v = &f->versions; | |
664 | ✗ | const CheckasmPerf *p = &v->perf; | |
665 | ✗ | const double baseline = avg_cycles_per_call(p); | |
666 | double decicycles; | ||
667 | |||
668 | ✗ | print_benchs(f->child[0]); | |
669 | |||
670 | do { | ||
671 | ✗ | if (p->iterations) { | |
672 | ✗ | p = &v->perf; | |
673 | ✗ | decicycles = avg_cycles_per_call(p); | |
674 | ✗ | if (state.csv || state.tsv) { | |
675 | ✗ | const char sep = state.csv ? ',' : '\t'; | |
676 | ✗ | printf("%s%c%s%c%.1f\n", f->name, sep, | |
677 | cpu_suffix(v->cpu), sep, | ||
678 | decicycles / 10.0); | ||
679 | } else { | ||
680 | ✗ | const int pad_length = 10 + 50 - | |
681 | ✗ | printf("%s_%s:", f->name, cpu_suffix(v->cpu)); | |
682 | ✗ | const double ratio = decicycles ? | |
683 | ✗ | baseline / decicycles : 0.0; | |
684 | ✗ | printf("%*.1f (%5.2fx)\n", FFMAX(pad_length, 0), | |
685 | decicycles / 10.0, ratio); | ||
686 | } | ||
687 | } | ||
688 | ✗ | } while ((v = v->next)); | |
689 | |||
690 | ✗ | print_benchs(f->child[1]); | |
691 | } | ||
692 | ✗ | } | |
693 | |||
694 | /* ASCIIbetical sort except preserving natural order for numbers */ | ||
695 | 1608536 | static int cmp_func_names(const char *a, const char *b) | |
696 | { | ||
697 | 1608536 | const char *start = a; | |
698 | int ascii_diff, digit_diff; | ||
699 | |||
700 |
4/4✓ Branch 0 taken 26713690 times.
✓ Branch 1 taken 1451027 times.
✓ Branch 2 taken 26556181 times.
✓ Branch 3 taken 157509 times.
|
28164717 | for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++); |
701 |
4/4✓ Branch 0 taken 1645351 times.
✓ Branch 1 taken 1349203 times.
✓ Branch 2 taken 1386018 times.
✓ Branch 3 taken 259333 times.
|
2994554 | for (; av_isdigit(*a) && av_isdigit(*b); a++, b++); |
702 | |||
703 |
6/6✓ Branch 0 taken 1534372 times.
✓ Branch 1 taken 74164 times.
✓ Branch 2 taken 1112921 times.
✓ Branch 3 taken 421451 times.
✓ Branch 4 taken 395066 times.
✓ Branch 5 taken 717855 times.
|
1608536 | if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b))) |
704 | 395066 | return digit_diff; | |
705 | |||
706 | 1213470 | return ascii_diff; | |
707 | } | ||
708 | |||
709 | /* Perform a tree rotation in the specified direction and return the new root */ | ||
710 | 12044 | static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir) | |
711 | { | ||
712 | 12044 | CheckasmFunc *r = f->child[dir^1]; | |
713 | 12044 | f->child[dir^1] = r->child[dir]; | |
714 | 12044 | r->child[dir] = f; | |
715 | 12044 | r->color = f->color; | |
716 | 12044 | f->color = 0; | |
717 | 12044 | return r; | |
718 | } | ||
719 | |||
720 | #define is_red(f) ((f) && !(f)->color) | ||
721 | |||
722 | /* Balance a left-leaning red-black tree at the specified node */ | ||
723 | 116230 | static void balance_tree(CheckasmFunc **root) | |
724 | { | ||
725 | 116230 | CheckasmFunc *f = *root; | |
726 | |||
727 |
8/8✓ Branch 0 taken 110385 times.
✓ Branch 1 taken 5845 times.
✓ Branch 2 taken 46388 times.
✓ Branch 3 taken 63997 times.
✓ Branch 4 taken 41793 times.
✓ Branch 5 taken 4595 times.
✓ Branch 6 taken 9021 times.
✓ Branch 7 taken 32772 times.
|
116230 | if (is_red(f->child[0]) && is_red(f->child[1])) { |
728 | 9021 | f->color ^= 1; | |
729 | 9021 | f->child[0]->color = f->child[1]->color = 1; | |
730 | } | ||
731 | |||
732 |
7/8✓ Branch 0 taken 110385 times.
✓ Branch 1 taken 5845 times.
✓ Branch 2 taken 73018 times.
✓ Branch 3 taken 37367 times.
✓ Branch 4 taken 78863 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8845 times.
✓ Branch 7 taken 70018 times.
|
116230 | if (!is_red(f->child[0]) && is_red(f->child[1])) |
733 | 8845 | *root = rotate_tree(f, 0); /* Rotate left */ | |
734 |
7/8✓ Branch 0 taken 107385 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37367 times.
✓ Branch 3 taken 70018 times.
✓ Branch 4 taken 34353 times.
✓ Branch 5 taken 3014 times.
✓ Branch 6 taken 3199 times.
✓ Branch 7 taken 31154 times.
|
107385 | else if (is_red(f->child[0]) && is_red(f->child[0]->child[0])) |
735 | 3199 | *root = rotate_tree(f, 1); /* Rotate right */ | |
736 | 116230 | } | |
737 | |||
738 | /* Get a node with the specified name, creating it if it doesn't exist */ | ||
739 | 1621145 | static CheckasmFunc *get_func(CheckasmFunc **root, const char *name) | |
740 | { | ||
741 | 1621145 | CheckasmFunc *f = *root; | |
742 | |||
743 |
2/2✓ Branch 0 taken 1608536 times.
✓ Branch 1 taken 12609 times.
|
1621145 | if (f) { |
744 | /* Search the tree for a matching node */ | ||
745 | 1608536 | int cmp = cmp_func_names(name, f->name); | |
746 |
2/2✓ Branch 0 taken 1451027 times.
✓ Branch 1 taken 157509 times.
|
1608536 | if (cmp) { |
747 | 1451027 | f = get_func(&f->child[cmp > 0], name); | |
748 | |||
749 | /* Rebalance the tree on the way up if a new node was inserted */ | ||
750 |
2/2✓ Branch 0 taken 116230 times.
✓ Branch 1 taken 1334797 times.
|
1451027 | if (!f->versions.func) |
751 | 116230 | balance_tree(root); | |
752 | } | ||
753 | } else { | ||
754 | /* Allocate and insert a new node into the tree */ | ||
755 | 12609 | int name_length = strlen(name); | |
756 | 12609 | f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length); | |
757 | 12609 | memcpy(f->name, name, name_length + 1); | |
758 | } | ||
759 | |||
760 | 1621145 | return f; | |
761 | } | ||
762 | |||
763 | checkasm_context checkasm_context_buf; | ||
764 | |||
765 | /* Crash handling: attempt to catch crashes and handle them | ||
766 | * gracefully instead of just aborting abruptly. */ | ||
767 | #ifdef _WIN32 | ||
768 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) | ||
769 | static LONG NTAPI signal_handler(EXCEPTION_POINTERS *e) { | ||
770 | int s; | ||
771 | |||
772 | if (!state.catch_signals) | ||
773 | return EXCEPTION_CONTINUE_SEARCH; | ||
774 | |||
775 | switch (e->ExceptionRecord->ExceptionCode) { | ||
776 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: | ||
777 | case EXCEPTION_INT_DIVIDE_BY_ZERO: | ||
778 | s = SIGFPE; | ||
779 | break; | ||
780 | case EXCEPTION_ILLEGAL_INSTRUCTION: | ||
781 | case EXCEPTION_PRIV_INSTRUCTION: | ||
782 | s = SIGILL; | ||
783 | break; | ||
784 | case EXCEPTION_ACCESS_VIOLATION: | ||
785 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: | ||
786 | case EXCEPTION_DATATYPE_MISALIGNMENT: | ||
787 | case EXCEPTION_STACK_OVERFLOW: | ||
788 | s = SIGSEGV; | ||
789 | break; | ||
790 | case EXCEPTION_IN_PAGE_ERROR: | ||
791 | s = SIGBUS; | ||
792 | break; | ||
793 | default: | ||
794 | return EXCEPTION_CONTINUE_SEARCH; | ||
795 | } | ||
796 | state.catch_signals = 0; | ||
797 | checkasm_load_context(s); | ||
798 | return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */ | ||
799 | } | ||
800 | #endif | ||
801 | #elif !defined(_WASI_EMULATED_SIGNAL) | ||
802 | static void signal_handler(int s); | ||
803 | |||
804 | static const struct sigaction signal_handler_act = { | ||
805 | .sa_handler = signal_handler, | ||
806 | .sa_flags = SA_RESETHAND, | ||
807 | }; | ||
808 | |||
809 | ✗ | static void signal_handler(int s) { | |
810 | ✗ | if (state.catch_signals) { | |
811 | ✗ | state.catch_signals = 0; | |
812 | ✗ | sigaction(s, &signal_handler_act, NULL); | |
813 | ✗ | checkasm_load_context(s); | |
814 | } | ||
815 | ✗ | } | |
816 | #endif | ||
817 | |||
818 | /* Compares a string with a wildcard pattern. */ | ||
819 | 85280 | static int wildstrcmp(const char *str, const char *pattern) | |
820 | { | ||
821 | 85280 | const char *wild = strchr(pattern, '*'); | |
822 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 85280 times.
|
85280 | if (wild) { |
823 | ✗ | const size_t len = wild - pattern; | |
824 | ✗ | if (strncmp(str, pattern, len)) return 1; | |
825 | ✗ | while (*++wild == '*'); | |
826 | ✗ | if (!*wild) return 0; | |
827 | ✗ | str += len; | |
828 | ✗ | while (*str && wildstrcmp(str, wild)) str++; | |
829 | ✗ | return !*str; | |
830 | } | ||
831 | 85280 | return strcmp(str, pattern); | |
832 | } | ||
833 | |||
834 | /* Perform tests and benchmarks for the specified cpu flag if supported by the host */ | ||
835 | 1520 | static void check_cpu_flag(const char *name, int flag) | |
836 | { | ||
837 | 1520 | int old_cpu_flag = state.cpu_flag; | |
838 | |||
839 | 1520 | flag |= old_cpu_flag; | |
840 | 1520 | av_force_cpu_flags(-1); | |
841 | 1520 | state.cpu_flag = flag & av_get_cpu_flags(); | |
842 | 1520 | av_force_cpu_flags(state.cpu_flag); | |
843 | |||
844 |
4/4✓ Branch 0 taken 1440 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 960 times.
✓ Branch 3 taken 480 times.
|
1520 | if (!flag || state.cpu_flag != old_cpu_flag) { |
845 | int i; | ||
846 | |||
847 | 1040 | state.cpu_flag_name = name; | |
848 |
2/2✓ Branch 0 taken 85280 times.
✓ Branch 1 taken 1040 times.
|
86320 | for (i = 0; tests[i].func; i++) { |
849 |
3/4✓ Branch 0 taken 85280 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 84240 times.
✓ Branch 4 taken 1040 times.
|
85280 | if (state.test_pattern && wildstrcmp(tests[i].name, state.test_pattern)) |
850 | 84240 | continue; | |
851 | 1040 | state.current_test_name = tests[i].name; | |
852 | 1040 | tests[i].func(); | |
853 | } | ||
854 | } | ||
855 | 1520 | } | |
856 | |||
857 | /* Print the name of the current CPU flag, but only do it once */ | ||
858 | 433 | static void print_cpu_name(void) | |
859 | { | ||
860 |
2/2✓ Branch 0 taken 207 times.
✓ Branch 1 taken 226 times.
|
433 | if (state.cpu_flag_name) { |
861 | 207 | color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name); | |
862 | 207 | state.cpu_flag_name = NULL; | |
863 | } | ||
864 | 433 | } | |
865 | |||
866 | #if CONFIG_LINUX_PERF | ||
867 | static int bench_init_linux(void) | ||
868 | { | ||
869 | struct perf_event_attr attr = { | ||
870 | .type = PERF_TYPE_HARDWARE, | ||
871 | .size = sizeof(struct perf_event_attr), | ||
872 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
873 | .disabled = 1, // start counting only on demand | ||
874 | .exclude_kernel = 1, | ||
875 | .exclude_hv = 1, | ||
876 | #if !ARCH_X86 | ||
877 | .exclude_guest = 1, | ||
878 | #endif | ||
879 | }; | ||
880 | |||
881 | fprintf(stderr, "benchmarking with Linux Perf Monitoring API\n"); | ||
882 | |||
883 | state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); | ||
884 | if (state.sysfd == -1) { | ||
885 | perror("perf_event_open"); | ||
886 | return -1; | ||
887 | } | ||
888 | return 0; | ||
889 | } | ||
890 | #elif CONFIG_MACOS_KPERF | ||
891 | static int bench_init_kperf(void) | ||
892 | { | ||
893 | ff_kperf_init(); | ||
894 | return 0; | ||
895 | } | ||
896 | #else | ||
897 | ✗ | static int bench_init_ffmpeg(void) | |
898 | { | ||
899 | #ifdef AV_READ_TIME | ||
900 | ✗ | if (!checkasm_save_context()) { | |
901 | ✗ | checkasm_set_signal_handler_state(1); | |
902 | ✗ | AV_READ_TIME(); | |
903 | ✗ | checkasm_set_signal_handler_state(0); | |
904 | } else { | ||
905 | ✗ | fprintf(stderr, "checkasm: unable to execute platform specific timer\n"); | |
906 | ✗ | return -1; | |
907 | } | ||
908 | ✗ | fprintf(stderr, "benchmarking with native FFmpeg timers\n"); | |
909 | ✗ | return 0; | |
910 | #else | ||
911 | fprintf(stderr, "checkasm: --bench is not supported on your system\n"); | ||
912 | return -1; | ||
913 | #endif | ||
914 | } | ||
915 | #endif | ||
916 | |||
917 | ✗ | static int bench_init(void) | |
918 | { | ||
919 | #if CONFIG_LINUX_PERF | ||
920 | int ret = bench_init_linux(); | ||
921 | #elif CONFIG_MACOS_KPERF | ||
922 | int ret = bench_init_kperf(); | ||
923 | #else | ||
924 | ✗ | int ret = bench_init_ffmpeg(); | |
925 | #endif | ||
926 | ✗ | if (ret < 0) | |
927 | ✗ | return ret; | |
928 | |||
929 | ✗ | state.nop_time = measure_nop_time(); | |
930 | ✗ | fprintf(stderr, "nop: %d.%d\n", state.nop_time/10, state.nop_time%10); | |
931 | ✗ | return 0; | |
932 | } | ||
933 | |||
934 | 80 | static void bench_uninit(void) | |
935 | { | ||
936 | #if CONFIG_LINUX_PERF | ||
937 | close(state.sysfd); | ||
938 | #endif | ||
939 | 80 | } | |
940 | |||
941 | ✗ | static int usage(const char *path) | |
942 | { | ||
943 | ✗ | fprintf(stderr, | |
944 | "Usage: %s [options...] [seed]\n" | ||
945 | " --test=<pattern> Run specific test.\n" | ||
946 | " --bench Run benchmark.\n" | ||
947 | " --csv, --tsv Output results in rows of comma or tab separated values.\n" | ||
948 | " --runs=<ptwo> Manual number of benchmark iterations to run 2**<ptwo>.\n" | ||
949 | " --verbose Increase verbosity.\n", | ||
950 | path); | ||
951 | ✗ | return 1; | |
952 | } | ||
953 | |||
954 | 80 | int main(int argc, char *argv[]) | |
955 | { | ||
956 | 80 | unsigned int seed = av_get_random_seed(); | |
957 | 80 | int i, ret = 0; | |
958 | 80 | char arch_info_buf[50] = ""; | |
959 | |||
960 | #ifdef _WIN32 | ||
961 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) | ||
962 | AddVectoredExceptionHandler(0, signal_handler); | ||
963 | #endif | ||
964 | #elif !defined(_WASI_EMULATED_SIGNAL) | ||
965 | 80 | sigaction(SIGBUS, &signal_handler_act, NULL); | |
966 | 80 | sigaction(SIGFPE, &signal_handler_act, NULL); | |
967 | 80 | sigaction(SIGILL, &signal_handler_act, NULL); | |
968 | 80 | sigaction(SIGSEGV, &signal_handler_act, NULL); | |
969 | #endif | ||
970 | #if HAVE_PRCTL && defined(PR_SET_UNALIGN) | ||
971 | 80 | prctl(PR_SET_UNALIGN, PR_UNALIGN_SIGBUS); | |
972 | #endif | ||
973 | #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL | ||
974 | if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags())) | ||
975 | checkasm_checked_call = checkasm_checked_call_vfp; | ||
976 | #endif | ||
977 | |||
978 |
2/4✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 80 times.
|
80 | if (!tests[0].func || !cpus[0].flag) { |
979 | ✗ | fprintf(stderr, "checkasm: no tests to perform\n"); | |
980 | ✗ | return 0; | |
981 | } | ||
982 | |||
983 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 80 times.
|
160 | for (i = 1; i < argc; i++) { |
984 | 80 | const char *arg = argv[i]; | |
985 | unsigned long l; | ||
986 | char *end; | ||
987 | |||
988 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (!strncmp(arg, "--bench", 7)) { |
989 | ✗ | if (bench_init() < 0) | |
990 | ✗ | return 1; | |
991 | ✗ | if (arg[7] == '=') { | |
992 | ✗ | state.bench_pattern = arg + 8; | |
993 | ✗ | state.bench_pattern_len = strlen(state.bench_pattern); | |
994 | } else | ||
995 | ✗ | state.bench_pattern = "*"; | |
996 |
1/2✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
|
80 | } else if (!strncmp(arg, "--test=", 7)) { |
997 | 80 | state.test_pattern = arg + 7; | |
998 | ✗ | } else if (!strcmp(arg, "--csv")) { | |
999 | ✗ | state.csv = 1; state.tsv = 0; | |
1000 | ✗ | } else if (!strcmp(arg, "--tsv")) { | |
1001 | ✗ | state.csv = 0; state.tsv = 1; | |
1002 | ✗ | } else if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) { | |
1003 | ✗ | state.verbose = 1; | |
1004 | ✗ | } else if (!strncmp(arg, "--runs=", 7)) { | |
1005 | ✗ | l = strtoul(arg + 7, &end, 10); | |
1006 | ✗ | if (*end == '\0') { | |
1007 | ✗ | if (l > 30) { | |
1008 | ✗ | fprintf(stderr, "checkasm: error: runs exponent must be within the range 0 <= 30\n"); | |
1009 | ✗ | usage(argv[0]); | |
1010 | } | ||
1011 | ✗ | bench_runs = 1U << l; | |
1012 | } else { | ||
1013 | ✗ | return usage(argv[0]); | |
1014 | } | ||
1015 | ✗ | } else if ((l = strtoul(arg, &end, 10)) <= UINT_MAX && | |
1016 | ✗ | *end == '\0') { | |
1017 | ✗ | seed = l; | |
1018 | } else { | ||
1019 | ✗ | return usage(argv[0]); | |
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | #if ARCH_AARCH64 && HAVE_SVE | ||
1024 | if (have_sve(av_get_cpu_flags())) | ||
1025 | snprintf(arch_info_buf, sizeof(arch_info_buf), | ||
1026 | "SVE %d bits, ", 8 * ff_aarch64_sve_length()); | ||
1027 | #elif ARCH_RISCV && HAVE_RVV | ||
1028 | if (av_get_cpu_flags() & AV_CPU_FLAG_RVV_I32) | ||
1029 | snprintf(arch_info_buf, sizeof (arch_info_buf), | ||
1030 | "%zu-bit vectors, ", 8 * ff_get_rv_vlenb()); | ||
1031 | #endif | ||
1032 | 80 | fprintf(stderr, "checkasm: %susing random seed %u\n", arch_info_buf, seed); | |
1033 | 80 | av_lfg_init(&checkasm_lfg, seed); | |
1034 | |||
1035 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (state.bench_pattern) |
1036 | ✗ | fprintf(stderr, "checkasm: bench runs %" PRIu64 " (1 << %i)\n", bench_runs, av_log2(bench_runs)); | |
1037 | |||
1038 | 80 | check_cpu_flag(NULL, 0); | |
1039 |
2/2✓ Branch 0 taken 1440 times.
✓ Branch 1 taken 80 times.
|
1520 | for (i = 0; cpus[i].flag; i++) |
1040 | 1440 | check_cpu_flag(cpus[i].name, cpus[i].flag); | |
1041 | |||
1042 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (state.num_failed) { |
1043 | ✗ | fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked); | |
1044 | ✗ | ret = 1; | |
1045 | } else { | ||
1046 | 80 | fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked); | |
1047 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (state.bench_pattern) { |
1048 | ✗ | print_benchs(state.funcs); | |
1049 | } | ||
1050 | } | ||
1051 | |||
1052 | 80 | destroy_func_tree(state.funcs); | |
1053 | 80 | bench_uninit(); | |
1054 | 80 | return ret; | |
1055 | } | ||
1056 | |||
1057 | /* Decide whether or not the specified function needs to be tested and | ||
1058 | * allocate/initialize data structures if needed. Returns a pointer to a | ||
1059 | * reference function if the function should be tested, otherwise NULL */ | ||
1060 | 170326 | void *checkasm_check_func(void *func, const char *name, ...) | |
1061 | { | ||
1062 | char name_buf[256]; | ||
1063 | 170326 | void *ref = func; | |
1064 | CheckasmFuncVersion *v; | ||
1065 | int name_length; | ||
1066 | va_list arg; | ||
1067 | |||
1068 | 170326 | va_start(arg, name); | |
1069 | 170326 | name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg); | |
1070 | 170326 | va_end(arg); | |
1071 | |||
1072 |
4/6✓ Branch 0 taken 170118 times.
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 170118 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 170118 times.
|
170326 | if (!func || name_length <= 0 || name_length >= sizeof(name_buf)) |
1073 | 208 | return NULL; | |
1074 | |||
1075 | 170118 | state.current_func = get_func(&state.funcs, name_buf); | |
1076 | 170118 | state.funcs->color = 1; | |
1077 | 170118 | v = &state.current_func->versions; | |
1078 | |||
1079 |
2/2✓ Branch 0 taken 157509 times.
✓ Branch 1 taken 12609 times.
|
170118 | if (v->func) { |
1080 | CheckasmFuncVersion *prev; | ||
1081 | do { | ||
1082 | /* Only test functions that haven't already been tested */ | ||
1083 |
2/2✓ Branch 0 taken 142644 times.
✓ Branch 1 taken 67807 times.
|
210451 | if (v->func == func) |
1084 | 142644 | return NULL; | |
1085 | |||
1086 |
1/2✓ Branch 0 taken 67807 times.
✗ Branch 1 not taken.
|
67807 | if (v->ok) |
1087 | 67807 | ref = v->func; | |
1088 | |||
1089 | 67807 | prev = v; | |
1090 |
2/2✓ Branch 0 taken 52942 times.
✓ Branch 1 taken 14865 times.
|
67807 | } while ((v = v->next)); |
1091 | |||
1092 | 14865 | v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion)); | |
1093 | } | ||
1094 | |||
1095 | 27474 | v->func = func; | |
1096 | 27474 | v->ok = 1; | |
1097 | 27474 | v->cpu = state.cpu_flag; | |
1098 | 27474 | state.current_func_ver = v; | |
1099 | |||
1100 |
2/2✓ Branch 0 taken 14865 times.
✓ Branch 1 taken 12609 times.
|
27474 | if (state.cpu_flag) |
1101 | 14865 | state.num_checked++; | |
1102 | |||
1103 | 27474 | return ref; | |
1104 | } | ||
1105 | |||
1106 | /* Decide whether or not the current function needs to be benchmarked */ | ||
1107 | 40561 | int checkasm_bench_func(void) | |
1108 | { | ||
1109 |
2/6✓ Branch 0 taken 40561 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40561 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
40561 | return !state.num_failed && state.bench_pattern && |
1110 | ✗ | !wildstrcmp(state.current_func->name, state.bench_pattern); | |
1111 | } | ||
1112 | |||
1113 | /* Indicate that the current test has failed, return whether verbose printing | ||
1114 | * is requested. */ | ||
1115 | ✗ | int checkasm_fail_func(const char *msg, ...) | |
1116 | { | ||
1117 | ✗ | if (state.current_func_ver && state.current_func_ver->cpu && | |
1118 | ✗ | state.current_func_ver->ok) | |
1119 | { | ||
1120 | va_list arg; | ||
1121 | |||
1122 | ✗ | print_cpu_name(); | |
1123 | ✗ | fprintf(stderr, " %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu)); | |
1124 | ✗ | va_start(arg, msg); | |
1125 | ✗ | vfprintf(stderr, msg, arg); | |
1126 | ✗ | va_end(arg); | |
1127 | ✗ | fprintf(stderr, ")\n"); | |
1128 | |||
1129 | ✗ | state.current_func_ver->ok = 0; | |
1130 | ✗ | state.num_failed++; | |
1131 | } | ||
1132 | ✗ | return state.verbose; | |
1133 | } | ||
1134 | |||
1135 | 318184 | void checkasm_set_signal_handler_state(int enabled) { | |
1136 | 318184 | state.catch_signals = enabled; | |
1137 | 318184 | } | |
1138 | |||
1139 | 170326 | int checkasm_handle_signal(int s) { | |
1140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 170326 times.
|
170326 | if (s) { |
1141 | #ifdef __GLIBC__ | ||
1142 | ✗ | checkasm_fail_func("fatal signal %d: %s", s, strsignal(s)); | |
1143 | #else | ||
1144 | checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" : | ||
1145 | s == SIGILL ? "illegal instruction" : | ||
1146 | s == SIGBUS ? "bus error" : | ||
1147 | "segmentation fault"); | ||
1148 | #endif | ||
1149 | } | ||
1150 | 170326 | return s; | |
1151 | } | ||
1152 | |||
1153 | /* Get the benchmark context of the current function */ | ||
1154 | ✗ | CheckasmPerf *checkasm_get_perf_context(void) | |
1155 | { | ||
1156 | ✗ | CheckasmPerf *perf = &state.current_func_ver->perf; | |
1157 | ✗ | memset(perf, 0, sizeof(*perf)); | |
1158 | ✗ | perf->sysfd = state.sysfd; | |
1159 | ✗ | return perf; | |
1160 | } | ||
1161 | |||
1162 | /* Print the outcome of all tests performed since the last time this function was called */ | ||
1163 | 3099 | void checkasm_report(const char *name, ...) | |
1164 | { | ||
1165 | static int prev_checked, prev_failed, max_length; | ||
1166 | |||
1167 |
2/2✓ Branch 0 taken 433 times.
✓ Branch 1 taken 2666 times.
|
3099 | if (state.num_checked > prev_checked) { |
1168 | 433 | int pad_length = max_length + 4; | |
1169 | va_list arg; | ||
1170 | |||
1171 | 433 | print_cpu_name(); | |
1172 | 433 | pad_length -= fprintf(stderr, " - %s.", state.current_test_name); | |
1173 | 433 | va_start(arg, name); | |
1174 | 433 | pad_length -= vfprintf(stderr, name, arg); | |
1175 | 433 | va_end(arg); | |
1176 | 433 | fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '['); | |
1177 | |||
1178 |
1/2✓ Branch 0 taken 433 times.
✗ Branch 1 not taken.
|
433 | if (state.num_failed == prev_failed) |
1179 | 433 | color_printf(COLOR_GREEN, "OK"); | |
1180 | else | ||
1181 | ✗ | color_printf(COLOR_RED, "FAILED"); | |
1182 | 433 | fprintf(stderr, "]\n"); | |
1183 | |||
1184 | 433 | prev_checked = state.num_checked; | |
1185 | 433 | prev_failed = state.num_failed; | |
1186 |
2/2✓ Branch 0 taken 243 times.
✓ Branch 1 taken 2423 times.
|
2666 | } else if (!state.cpu_flag) { |
1187 | /* Calculate the amount of padding required to make the output vertically aligned */ | ||
1188 | 243 | int length = strlen(state.current_test_name); | |
1189 | va_list arg; | ||
1190 | |||
1191 | 243 | va_start(arg, name); | |
1192 | 243 | length += vsnprintf(NULL, 0, name, arg); | |
1193 | 243 | va_end(arg); | |
1194 | |||
1195 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 107 times.
|
243 | if (length > max_length) |
1196 | 136 | max_length = length; | |
1197 | } | ||
1198 | 3099 | } | |
1199 | |||
1200 | ✗ | static int check_err(const char *file, int line, | |
1201 | const char *name, int w, int h, | ||
1202 | int *err) | ||
1203 | { | ||
1204 | ✗ | if (*err) | |
1205 | ✗ | return 0; | |
1206 | ✗ | if (!checkasm_fail_func("%s:%d", file, line)) | |
1207 | ✗ | return 1; | |
1208 | ✗ | *err = 1; | |
1209 | ✗ | fprintf(stderr, "%s (%dx%d):\n", name, w, h); | |
1210 | ✗ | return 0; | |
1211 | } | ||
1212 | |||
1213 | #define DEF_CHECKASM_CHECK_BODY(compare, type, fmt) \ | ||
1214 | do { \ | ||
1215 | int64_t aligned_w = (w - 1LL + align_w) & ~(align_w - 1); \ | ||
1216 | int64_t aligned_h = (h - 1LL + align_h) & ~(align_h - 1); \ | ||
1217 | int err = 0; \ | ||
1218 | int y = 0; \ | ||
1219 | av_assert0(aligned_w == (int32_t)aligned_w);\ | ||
1220 | av_assert0(aligned_h == (int32_t)aligned_h);\ | ||
1221 | stride1 /= sizeof(*buf1); \ | ||
1222 | stride2 /= sizeof(*buf2); \ | ||
1223 | for (y = 0; y < h; y++) \ | ||
1224 | if (!compare(&buf1[y*stride1], &buf2[y*stride2], w)) \ | ||
1225 | break; \ | ||
1226 | if (y != h) { \ | ||
1227 | if (check_err(file, line, name, w, h, &err)) \ | ||
1228 | return 1; \ | ||
1229 | for (y = 0; y < h; y++) { \ | ||
1230 | for (int x = 0; x < w; x++) \ | ||
1231 | fprintf(stderr, " " fmt, buf1[x]); \ | ||
1232 | fprintf(stderr, " "); \ | ||
1233 | for (int x = 0; x < w; x++) \ | ||
1234 | fprintf(stderr, " " fmt, buf2[x]); \ | ||
1235 | fprintf(stderr, " "); \ | ||
1236 | for (int x = 0; x < w; x++) \ | ||
1237 | fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \ | ||
1238 | buf1 += stride1; \ | ||
1239 | buf2 += stride2; \ | ||
1240 | fprintf(stderr, "\n"); \ | ||
1241 | } \ | ||
1242 | buf1 -= h*stride1; \ | ||
1243 | buf2 -= h*stride2; \ | ||
1244 | } \ | ||
1245 | for (y = -padding; y < 0; y++) \ | ||
1246 | if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \ | ||
1247 | w + 2*padding)) { \ | ||
1248 | if (check_err(file, line, name, w, h, &err)) \ | ||
1249 | return 1; \ | ||
1250 | fprintf(stderr, " overwrite above\n"); \ | ||
1251 | break; \ | ||
1252 | } \ | ||
1253 | for (y = aligned_h; y < aligned_h + padding; y++) \ | ||
1254 | if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \ | ||
1255 | w + 2*padding)) { \ | ||
1256 | if (check_err(file, line, name, w, h, &err)) \ | ||
1257 | return 1; \ | ||
1258 | fprintf(stderr, " overwrite below\n"); \ | ||
1259 | break; \ | ||
1260 | } \ | ||
1261 | for (y = 0; y < h; y++) \ | ||
1262 | if (!compare(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \ | ||
1263 | padding)) { \ | ||
1264 | if (check_err(file, line, name, w, h, &err)) \ | ||
1265 | return 1; \ | ||
1266 | fprintf(stderr, " overwrite left\n"); \ | ||
1267 | break; \ | ||
1268 | } \ | ||
1269 | for (y = 0; y < h; y++) \ | ||
1270 | if (!compare(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \ | ||
1271 | padding)) { \ | ||
1272 | if (check_err(file, line, name, w, h, &err)) \ | ||
1273 | return 1; \ | ||
1274 | fprintf(stderr, " overwrite right\n"); \ | ||
1275 | break; \ | ||
1276 | } \ | ||
1277 | return err; \ | ||
1278 | } while (0) | ||
1279 | |||
1280 | #define cmp_int(a, b, len) (!memcmp(a, b, (len) * sizeof(*(a)))) | ||
1281 | #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \ | ||
1282 | int checkasm_check_##type(const char *file, int line, \ | ||
1283 | const type *buf1, ptrdiff_t stride1, \ | ||
1284 | const type *buf2, ptrdiff_t stride2, \ | ||
1285 | int w, int h, const char *name, \ | ||
1286 | int align_w, int align_h, \ | ||
1287 | int padding) \ | ||
1288 | { \ | ||
1289 | DEF_CHECKASM_CHECK_BODY(cmp_int, type, fmt); \ | ||
1290 | } | ||
1291 | |||
1292 |
18/46✗ Branch 0 not taken.
✓ Branch 1 taken 8131 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8131 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 231700 times.
✓ Branch 10 taken 231700 times.
✓ Branch 11 taken 8131 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8131 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 39296 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 39 taken 39296 times.
✓ Branch 40 taken 8131 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 39296 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 47 taken 39296 times.
✓ Branch 48 taken 8131 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 231700 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 231700 times.
✓ Branch 56 taken 8131 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 231700 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 231700 times.
✓ Branch 64 taken 8131 times.
|
781823 | DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x") |
1293 |
18/46✗ Branch 0 not taken.
✓ Branch 1 taken 19626 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19626 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 556470 times.
✓ Branch 10 taken 556470 times.
✓ Branch 11 taken 19626 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 19626 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 115440 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 39 taken 115440 times.
✓ Branch 40 taken 19626 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 115440 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 47 taken 115440 times.
✓ Branch 48 taken 19626 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 556470 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 556470 times.
✓ Branch 56 taken 19626 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 556470 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 556470 times.
✓ Branch 64 taken 19626 times.
|
1919916 | DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x") |
1294 |
14/46✗ Branch 0 not taken.
✓ Branch 1 taken 658 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 658 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1316 times.
✓ Branch 10 taken 1316 times.
✓ Branch 11 taken 658 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 658 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 658 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 658 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 1316 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 1316 times.
✓ Branch 56 taken 658 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 1316 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 1316 times.
✓ Branch 64 taken 658 times.
|
4606 | DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x") |
1295 |
14/46✗ Branch 0 not taken.
✓ Branch 1 taken 550 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 550 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 14114 times.
✓ Branch 10 taken 14114 times.
✓ Branch 11 taken 550 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 550 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 550 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 550 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 14114 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 14114 times.
✓ Branch 56 taken 550 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 14114 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 14114 times.
✓ Branch 64 taken 550 times.
|
42892 | DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d") |
1296 |
14/46✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 64 times.
✓ Branch 10 taken 64 times.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✓ Branch 40 taken 4 times.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 4 times.
✗ Branch 49 not taken.
✓ Branch 50 taken 64 times.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 64 times.
✓ Branch 56 taken 4 times.
✗ Branch 57 not taken.
✓ Branch 58 taken 64 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✓ Branch 63 taken 64 times.
✓ Branch 64 taken 4 times.
|
196 | DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d") |
1297 | |||
1298 | 826 | int checkasm_check_float_ulp(const char *file, int line, | |
1299 | const float *buf1, ptrdiff_t stride1, | ||
1300 | const float *buf2, ptrdiff_t stride2, | ||
1301 | int w, int h, const char *name, | ||
1302 | unsigned max_ulp, int align_w, int align_h, | ||
1303 | int padding) | ||
1304 | { | ||
1305 | #define cmp_float(a, b, len) float_near_ulp_array(a, b, max_ulp, len) | ||
1306 |
14/46✗ Branch 0 not taken.
✓ Branch 1 taken 826 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 826 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1652 times.
✓ Branch 11 taken 1652 times.
✓ Branch 12 taken 826 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 826 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✓ Branch 42 taken 826 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 50 not taken.
✓ Branch 51 taken 826 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 1652 times.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 59 taken 1652 times.
✓ Branch 60 taken 826 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 1652 times.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✓ Branch 68 taken 1652 times.
✓ Branch 69 taken 826 times.
|
5782 | DEF_CHECKASM_CHECK_BODY(cmp_float, float, "%g"); |
1307 | #undef cmp_float | ||
1308 | } | ||
1309 |