f521d30b7cfff3657ae66615a46709a3d31ea50a
[platform/upstream/ffmpeg.git] / tests / checkasm / checkasm.c
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
23 #include "config.h"
24 #include "config_components.h"
25
26 #if CONFIG_LINUX_PERF
27 # ifndef _GNU_SOURCE
28 #  define _GNU_SOURCE // for syscall (performance monitoring API)
29 # endif
30 #endif
31
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "checkasm.h"
37 #include "libavutil/common.h"
38 #include "libavutil/cpu.h"
39 #include "libavutil/intfloat.h"
40 #include "libavutil/random_seed.h"
41
42 #if HAVE_IO_H
43 #include <io.h>
44 #endif
45
46 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
47 #include <windows.h>
48 #define COLOR_RED    FOREGROUND_RED
49 #define COLOR_GREEN  FOREGROUND_GREEN
50 #define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
51 #else
52 #define COLOR_RED    1
53 #define COLOR_GREEN  2
54 #define COLOR_YELLOW 3
55 #endif
56
57 #if HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60
61 #if !HAVE_ISATTY
62 #define isatty(fd) 1
63 #endif
64
65 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
66 #include "libavutil/arm/cpu.h"
67
68 void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
69 #endif
70
71 /* List of tests to invoke */
72 static const struct {
73     const char *name;
74     void (*func)(void);
75 } tests[] = {
76 #if CONFIG_AVCODEC
77     #if CONFIG_AAC_DECODER
78         { "aacpsdsp", checkasm_check_aacpsdsp },
79         { "sbrdsp",   checkasm_check_sbrdsp },
80     #endif
81     #if CONFIG_ALAC_DECODER
82         { "alacdsp", checkasm_check_alacdsp },
83     #endif
84     #if CONFIG_AUDIODSP
85         { "audiodsp", checkasm_check_audiodsp },
86     #endif
87     #if CONFIG_BLOCKDSP
88         { "blockdsp", checkasm_check_blockdsp },
89     #endif
90     #if CONFIG_BSWAPDSP
91         { "bswapdsp", checkasm_check_bswapdsp },
92     #endif
93     #if CONFIG_DCA_DECODER
94         { "synth_filter", checkasm_check_synth_filter },
95     #endif
96     #if CONFIG_EXR_DECODER
97         { "exrdsp", checkasm_check_exrdsp },
98     #endif
99     #if CONFIG_FLAC_DECODER
100         { "flacdsp", checkasm_check_flacdsp },
101     #endif
102     #if CONFIG_FMTCONVERT
103         { "fmtconvert", checkasm_check_fmtconvert },
104     #endif
105     #if CONFIG_G722DSP
106         { "g722dsp", checkasm_check_g722dsp },
107     #endif
108     #if CONFIG_H264CHROMA
109         { "h264chroma", checkasm_check_h264chroma },
110     #endif
111     #if CONFIG_H264DSP
112         { "h264dsp", checkasm_check_h264dsp },
113     #endif
114     #if CONFIG_H264PRED
115         { "h264pred", checkasm_check_h264pred },
116     #endif
117     #if CONFIG_H264QPEL
118         { "h264qpel", checkasm_check_h264qpel },
119     #endif
120     #if CONFIG_HEVC_DECODER
121         { "hevc_add_res", checkasm_check_hevc_add_res },
122         { "hevc_deblock", checkasm_check_hevc_deblock },
123         { "hevc_idct", checkasm_check_hevc_idct },
124         { "hevc_pel", checkasm_check_hevc_pel },
125         { "hevc_sao", checkasm_check_hevc_sao },
126     #endif
127     #if CONFIG_HUFFYUV_DECODER
128         { "huffyuvdsp", checkasm_check_huffyuvdsp },
129     #endif
130     #if CONFIG_IDCTDSP
131         { "idctdsp", checkasm_check_idctdsp },
132     #endif
133     #if CONFIG_JPEG2000_DECODER
134         { "jpeg2000dsp", checkasm_check_jpeg2000dsp },
135     #endif
136     #if CONFIG_HUFFYUVDSP
137         { "llviddsp", checkasm_check_llviddsp },
138     #endif
139     #if CONFIG_LLVIDENCDSP
140         { "llviddspenc", checkasm_check_llviddspenc },
141     #endif
142     #if CONFIG_LPC
143         { "lpc", checkasm_check_lpc },
144     #endif
145     #if CONFIG_ME_CMP
146         { "motion", checkasm_check_motion },
147     #endif
148     #if CONFIG_OPUS_DECODER
149         { "opusdsp", checkasm_check_opusdsp },
150     #endif
151     #if CONFIG_PIXBLOCKDSP
152         { "pixblockdsp", checkasm_check_pixblockdsp },
153     #endif
154     #if CONFIG_UTVIDEO_DECODER
155         { "utvideodsp", checkasm_check_utvideodsp },
156     #endif
157     #if CONFIG_V210_DECODER
158         { "v210dec", checkasm_check_v210dec },
159     #endif
160     #if CONFIG_V210_ENCODER
161         { "v210enc", checkasm_check_v210enc },
162     #endif
163     #if CONFIG_VC1DSP
164         { "vc1dsp", checkasm_check_vc1dsp },
165     #endif
166     #if CONFIG_VP8DSP
167         { "vp8dsp", checkasm_check_vp8dsp },
168     #endif
169     #if CONFIG_VP9_DECODER
170         { "vp9dsp", checkasm_check_vp9dsp },
171     #endif
172     #if CONFIG_VIDEODSP
173         { "videodsp", checkasm_check_videodsp },
174     #endif
175     #if CONFIG_VORBIS_DECODER
176         { "vorbisdsp", checkasm_check_vorbisdsp },
177     #endif
178 #endif
179 #if CONFIG_AVFILTER
180     #if CONFIG_AFIR_FILTER
181         { "af_afir", checkasm_check_afir },
182     #endif
183     #if CONFIG_BLEND_FILTER
184         { "vf_blend", checkasm_check_blend },
185     #endif
186     #if CONFIG_BWDIF_FILTER
187         { "vf_bwdif", checkasm_check_vf_bwdif },
188     #endif
189     #if CONFIG_COLORSPACE_FILTER
190         { "vf_colorspace", checkasm_check_colorspace },
191     #endif
192     #if CONFIG_EQ_FILTER
193         { "vf_eq", checkasm_check_vf_eq },
194     #endif
195     #if CONFIG_GBLUR_FILTER
196         { "vf_gblur", checkasm_check_vf_gblur },
197     #endif
198     #if CONFIG_HFLIP_FILTER
199         { "vf_hflip", checkasm_check_vf_hflip },
200     #endif
201     #if CONFIG_NLMEANS_FILTER
202         { "vf_nlmeans", checkasm_check_nlmeans },
203     #endif
204     #if CONFIG_THRESHOLD_FILTER
205         { "vf_threshold", checkasm_check_vf_threshold },
206     #endif
207     #if CONFIG_SOBEL_FILTER
208         { "vf_sobel", checkasm_check_vf_sobel },
209     #endif
210 #endif
211 #if CONFIG_SWSCALE
212     { "sw_gbrp", checkasm_check_sw_gbrp },
213     { "sw_rgb", checkasm_check_sw_rgb },
214     { "sw_scale", checkasm_check_sw_scale },
215 #endif
216 #if CONFIG_AVUTIL
217         { "fixed_dsp", checkasm_check_fixed_dsp },
218         { "float_dsp", checkasm_check_float_dsp },
219         { "av_tx",     checkasm_check_av_tx },
220 #endif
221     { NULL }
222 };
223
224 /* List of cpu flags to check */
225 static const struct {
226     const char *name;
227     const char *suffix;
228     int flag;
229 } cpus[] = {
230 #if   ARCH_AARCH64
231     { "ARMV8",    "armv8",    AV_CPU_FLAG_ARMV8 },
232     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
233     { "DOTPROD",  "dotprod",  AV_CPU_FLAG_DOTPROD },
234     { "I8MM",     "i8mm",     AV_CPU_FLAG_I8MM },
235 #elif ARCH_ARM
236     { "ARMV5TE",  "armv5te",  AV_CPU_FLAG_ARMV5TE },
237     { "ARMV6",    "armv6",    AV_CPU_FLAG_ARMV6 },
238     { "ARMV6T2",  "armv6t2",  AV_CPU_FLAG_ARMV6T2 },
239     { "VFP",      "vfp",      AV_CPU_FLAG_VFP },
240     { "VFP_VM",   "vfp_vm",   AV_CPU_FLAG_VFP_VM },
241     { "VFPV3",    "vfp3",     AV_CPU_FLAG_VFPV3 },
242     { "NEON",     "neon",     AV_CPU_FLAG_NEON },
243 #elif ARCH_PPC
244     { "ALTIVEC",  "altivec",  AV_CPU_FLAG_ALTIVEC },
245     { "VSX",      "vsx",      AV_CPU_FLAG_VSX },
246     { "POWER8",   "power8",   AV_CPU_FLAG_POWER8 },
247 #elif ARCH_RISCV
248     { "RVI",      "rvi",      AV_CPU_FLAG_RVI },
249     { "RVF",      "rvf",      AV_CPU_FLAG_RVF },
250     { "RVD",      "rvd",      AV_CPU_FLAG_RVD },
251     { "RVBaddr",  "rvb_a",    AV_CPU_FLAG_RVB_ADDR },
252     { "RVBbasic", "rvb_b",    AV_CPU_FLAG_RVB_BASIC },
253     { "RVVi32",   "rvv_i32",  AV_CPU_FLAG_RVV_I32 },
254     { "RVVf32",   "rvv_f32",  AV_CPU_FLAG_RVV_F32 },
255     { "RVVi64",   "rvv_i64",  AV_CPU_FLAG_RVV_I64 },
256     { "RVVf64",   "rvv_f64",  AV_CPU_FLAG_RVV_F64 },
257 #elif ARCH_MIPS
258     { "MMI",      "mmi",      AV_CPU_FLAG_MMI },
259     { "MSA",      "msa",      AV_CPU_FLAG_MSA },
260 #elif ARCH_X86
261     { "MMX",        "mmx",       AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
262     { "MMXEXT",     "mmxext",    AV_CPU_FLAG_MMXEXT },
263     { "3DNOW",      "3dnow",     AV_CPU_FLAG_3DNOW },
264     { "3DNOWEXT",   "3dnowext",  AV_CPU_FLAG_3DNOWEXT },
265     { "SSE",        "sse",       AV_CPU_FLAG_SSE },
266     { "SSE2",       "sse2",      AV_CPU_FLAG_SSE2|AV_CPU_FLAG_SSE2SLOW },
267     { "SSE3",       "sse3",      AV_CPU_FLAG_SSE3|AV_CPU_FLAG_SSE3SLOW },
268     { "SSSE3",      "ssse3",     AV_CPU_FLAG_SSSE3|AV_CPU_FLAG_ATOM },
269     { "SSE4.1",     "sse4",      AV_CPU_FLAG_SSE4 },
270     { "SSE4.2",     "sse42",     AV_CPU_FLAG_SSE42 },
271     { "AES-NI",     "aesni",     AV_CPU_FLAG_AESNI },
272     { "AVX",        "avx",       AV_CPU_FLAG_AVX },
273     { "XOP",        "xop",       AV_CPU_FLAG_XOP },
274     { "FMA3",       "fma3",      AV_CPU_FLAG_FMA3 },
275     { "FMA4",       "fma4",      AV_CPU_FLAG_FMA4 },
276     { "AVX2",       "avx2",      AV_CPU_FLAG_AVX2 },
277     { "AVX-512",    "avx512",    AV_CPU_FLAG_AVX512 },
278     { "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL },
279 #elif ARCH_LOONGARCH
280     { "LSX",      "lsx",      AV_CPU_FLAG_LSX },
281     { "LASX",     "lasx",     AV_CPU_FLAG_LASX },
282 #endif
283     { NULL }
284 };
285
286 typedef struct CheckasmFuncVersion {
287     struct CheckasmFuncVersion *next;
288     void *func;
289     int ok;
290     int cpu;
291     CheckasmPerf perf;
292 } CheckasmFuncVersion;
293
294 /* Binary search tree node */
295 typedef struct CheckasmFunc {
296     struct CheckasmFunc *child[2];
297     CheckasmFuncVersion versions;
298     uint8_t color; /* 0 = red, 1 = black */
299     char name[1];
300 } CheckasmFunc;
301
302 /* Internal state */
303 static struct {
304     CheckasmFunc *funcs;
305     CheckasmFunc *current_func;
306     CheckasmFuncVersion *current_func_ver;
307     const char *current_test_name;
308     const char *bench_pattern;
309     int bench_pattern_len;
310     int num_checked;
311     int num_failed;
312
313     /* perf */
314     int nop_time;
315     int sysfd;
316
317     int cpu_flag;
318     const char *cpu_flag_name;
319     const char *test_name;
320     int verbose;
321 } state;
322
323 /* PRNG state */
324 AVLFG checkasm_lfg;
325
326 /* float compare support code */
327 static int is_negative(union av_intfloat32 u)
328 {
329     return u.i >> 31;
330 }
331
332 int float_near_ulp(float a, float b, unsigned max_ulp)
333 {
334     union av_intfloat32 x, y;
335
336     x.f = a;
337     y.f = b;
338
339     if (is_negative(x) != is_negative(y)) {
340         // handle -0.0 == +0.0
341         return a == b;
342     }
343
344     if (llabs((int64_t)x.i - y.i) <= max_ulp)
345         return 1;
346
347     return 0;
348 }
349
350 int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
351                          unsigned len)
352 {
353     unsigned i;
354
355     for (i = 0; i < len; i++) {
356         if (!float_near_ulp(a[i], b[i], max_ulp))
357             return 0;
358     }
359     return 1;
360 }
361
362 int float_near_abs_eps(float a, float b, float eps)
363 {
364     float abs_diff = fabsf(a - b);
365     if (abs_diff < eps)
366         return 1;
367
368     fprintf(stderr, "test failed comparing %g with %g (abs diff=%g with EPS=%g)\n", a, b, abs_diff, eps);
369
370     return 0;
371 }
372
373 int float_near_abs_eps_array(const float *a, const float *b, float eps,
374                          unsigned len)
375 {
376     unsigned i;
377
378     for (i = 0; i < len; i++) {
379         if (!float_near_abs_eps(a[i], b[i], eps))
380             return 0;
381     }
382     return 1;
383 }
384
385 int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
386 {
387     return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
388 }
389
390 int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
391                          unsigned max_ulp, unsigned len)
392 {
393     unsigned i;
394
395     for (i = 0; i < len; i++) {
396         if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
397             return 0;
398     }
399     return 1;
400 }
401
402 int double_near_abs_eps(double a, double b, double eps)
403 {
404     double abs_diff = fabs(a - b);
405
406     return abs_diff < eps;
407 }
408
409 int double_near_abs_eps_array(const double *a, const double *b, double eps,
410                               unsigned len)
411 {
412     unsigned i;
413
414     for (i = 0; i < len; i++) {
415         if (!double_near_abs_eps(a[i], b[i], eps))
416             return 0;
417     }
418     return 1;
419 }
420
421 /* Print colored text to stderr if the terminal supports it */
422 static void color_printf(int color, const char *fmt, ...)
423 {
424     static int use_color = -1;
425     va_list arg;
426
427 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
428     static HANDLE con;
429     static WORD org_attributes;
430
431     if (use_color < 0) {
432         CONSOLE_SCREEN_BUFFER_INFO con_info;
433         con = GetStdHandle(STD_ERROR_HANDLE);
434         if (con && con != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(con, &con_info)) {
435             org_attributes = con_info.wAttributes;
436             use_color = 1;
437         } else
438             use_color = 0;
439     }
440     if (use_color)
441         SetConsoleTextAttribute(con, (org_attributes & 0xfff0) | (color & 0x0f));
442 #else
443     if (use_color < 0) {
444         const char *term = getenv("TERM");
445         use_color = term && strcmp(term, "dumb") && isatty(2);
446     }
447     if (use_color)
448         fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
449 #endif
450
451     va_start(arg, fmt);
452     vfprintf(stderr, fmt, arg);
453     va_end(arg);
454
455     if (use_color) {
456 #if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
457         SetConsoleTextAttribute(con, org_attributes);
458 #else
459         fprintf(stderr, "\x1b[0m");
460 #endif
461     }
462 }
463
464 /* Deallocate a tree */
465 static void destroy_func_tree(CheckasmFunc *f)
466 {
467     if (f) {
468         CheckasmFuncVersion *v = f->versions.next;
469         while (v) {
470             CheckasmFuncVersion *next = v->next;
471             free(v);
472             v = next;
473         }
474
475         destroy_func_tree(f->child[0]);
476         destroy_func_tree(f->child[1]);
477         free(f);
478     }
479 }
480
481 /* Allocate a zero-initialized block, clean up and exit on failure */
482 static void *checkasm_malloc(size_t size)
483 {
484     void *ptr = calloc(1, size);
485     if (!ptr) {
486         fprintf(stderr, "checkasm: malloc failed\n");
487         destroy_func_tree(state.funcs);
488         exit(1);
489     }
490     return ptr;
491 }
492
493 /* Get the suffix of the specified cpu flag */
494 static const char *cpu_suffix(int cpu)
495 {
496     int i = FF_ARRAY_ELEMS(cpus);
497
498     while (--i >= 0)
499         if (cpu & cpus[i].flag)
500             return cpus[i].suffix;
501
502     return "c";
503 }
504
505 static int cmp_nop(const void *a, const void *b)
506 {
507     return *(const uint16_t*)a - *(const uint16_t*)b;
508 }
509
510 /* Measure the overhead of the timing code (in decicycles) */
511 static int measure_nop_time(void)
512 {
513     uint16_t nops[10000];
514     int i, nop_sum = 0;
515     av_unused const int sysfd = state.sysfd;
516
517     uint64_t t = 0;
518     for (i = 0; i < 10000; i++) {
519         PERF_START(t);
520         PERF_STOP(t);
521         nops[i] = t;
522     }
523
524     qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
525     for (i = 2500; i < 7500; i++)
526         nop_sum += nops[i];
527
528     return nop_sum / 500;
529 }
530
531 /* Print benchmark results */
532 static void print_benchs(CheckasmFunc *f)
533 {
534     if (f) {
535         print_benchs(f->child[0]);
536
537         /* Only print functions with at least one assembly version */
538         if (f->versions.cpu || f->versions.next) {
539             CheckasmFuncVersion *v = &f->versions;
540             do {
541                 CheckasmPerf *p = &v->perf;
542                 if (p->iterations) {
543                     int decicycles = (10*p->cycles/p->iterations - state.nop_time) / 4;
544                     printf("%s_%s: %d.%d\n", f->name, cpu_suffix(v->cpu), decicycles/10, decicycles%10);
545                 }
546             } while ((v = v->next));
547         }
548
549         print_benchs(f->child[1]);
550     }
551 }
552
553 /* ASCIIbetical sort except preserving natural order for numbers */
554 static int cmp_func_names(const char *a, const char *b)
555 {
556     const char *start = a;
557     int ascii_diff, digit_diff;
558
559     for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
560     for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
561
562     if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
563         return digit_diff;
564
565     return ascii_diff;
566 }
567
568 /* Perform a tree rotation in the specified direction and return the new root */
569 static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
570 {
571     CheckasmFunc *r = f->child[dir^1];
572     f->child[dir^1] = r->child[dir];
573     r->child[dir] = f;
574     r->color = f->color;
575     f->color = 0;
576     return r;
577 }
578
579 #define is_red(f) ((f) && !(f)->color)
580
581 /* Balance a left-leaning red-black tree at the specified node */
582 static void balance_tree(CheckasmFunc **root)
583 {
584     CheckasmFunc *f = *root;
585
586     if (is_red(f->child[0]) && is_red(f->child[1])) {
587         f->color ^= 1;
588         f->child[0]->color = f->child[1]->color = 1;
589     }
590
591     if (!is_red(f->child[0]) && is_red(f->child[1]))
592         *root = rotate_tree(f, 0); /* Rotate left */
593     else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
594         *root = rotate_tree(f, 1); /* Rotate right */
595 }
596
597 /* Get a node with the specified name, creating it if it doesn't exist */
598 static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
599 {
600     CheckasmFunc *f = *root;
601
602     if (f) {
603         /* Search the tree for a matching node */
604         int cmp = cmp_func_names(name, f->name);
605         if (cmp) {
606             f = get_func(&f->child[cmp > 0], name);
607
608             /* Rebalance the tree on the way up if a new node was inserted */
609             if (!f->versions.func)
610                 balance_tree(root);
611         }
612     } else {
613         /* Allocate and insert a new node into the tree */
614         int name_length = strlen(name);
615         f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
616         memcpy(f->name, name, name_length + 1);
617     }
618
619     return f;
620 }
621
622 /* Perform tests and benchmarks for the specified cpu flag if supported by the host */
623 static void check_cpu_flag(const char *name, int flag)
624 {
625     int old_cpu_flag = state.cpu_flag;
626
627     flag |= old_cpu_flag;
628     av_force_cpu_flags(-1);
629     state.cpu_flag = flag & av_get_cpu_flags();
630     av_force_cpu_flags(state.cpu_flag);
631
632     if (!flag || state.cpu_flag != old_cpu_flag) {
633         int i;
634
635         state.cpu_flag_name = name;
636         for (i = 0; tests[i].func; i++) {
637             if (state.test_name && strcmp(tests[i].name, state.test_name))
638                 continue;
639             state.current_test_name = tests[i].name;
640             tests[i].func();
641         }
642     }
643 }
644
645 /* Print the name of the current CPU flag, but only do it once */
646 static void print_cpu_name(void)
647 {
648     if (state.cpu_flag_name) {
649         color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
650         state.cpu_flag_name = NULL;
651     }
652 }
653
654 #if CONFIG_LINUX_PERF
655 static int bench_init_linux(void)
656 {
657     struct perf_event_attr attr = {
658         .type           = PERF_TYPE_HARDWARE,
659         .size           = sizeof(struct perf_event_attr),
660         .config         = PERF_COUNT_HW_CPU_CYCLES,
661         .disabled       = 1, // start counting only on demand
662         .exclude_kernel = 1,
663         .exclude_hv     = 1,
664     };
665
666     printf("benchmarking with Linux Perf Monitoring API\n");
667
668     state.sysfd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
669     if (state.sysfd == -1) {
670         perror("perf_event_open");
671         return -1;
672     }
673     return 0;
674 }
675 #elif CONFIG_MACOS_KPERF
676 static int bench_init_kperf(void)
677 {
678     ff_kperf_init();
679     return 0;
680 }
681 #else
682 static int bench_init_ffmpeg(void)
683 {
684 #ifdef AV_READ_TIME
685     printf("benchmarking with native FFmpeg timers\n");
686     return 0;
687 #else
688     fprintf(stderr, "checkasm: --bench is not supported on your system\n");
689     return -1;
690 #endif
691 }
692 #endif
693
694 static int bench_init(void)
695 {
696 #if CONFIG_LINUX_PERF
697     int ret = bench_init_linux();
698 #elif CONFIG_MACOS_KPERF
699     int ret = bench_init_kperf();
700 #else
701     int ret = bench_init_ffmpeg();
702 #endif
703     if (ret < 0)
704         return ret;
705
706     state.nop_time = measure_nop_time();
707     printf("nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
708     return 0;
709 }
710
711 static void bench_uninit(void)
712 {
713 #if CONFIG_LINUX_PERF
714     if (state.sysfd > 0)
715         close(state.sysfd);
716 #endif
717 }
718
719 static int usage(const char *path)
720 {
721     fprintf(stderr,
722             "Usage: %s [--bench] [--test=<pattern>] [--verbose] [seed]\n",
723             path);
724     return 1;
725 }
726
727 int main(int argc, char *argv[])
728 {
729     unsigned int seed = av_get_random_seed();
730     int i, ret = 0;
731
732 #if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
733     if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
734         checkasm_checked_call = checkasm_checked_call_vfp;
735 #endif
736
737     if (!tests[0].func || !cpus[0].flag) {
738         fprintf(stderr, "checkasm: no tests to perform\n");
739         return 0;
740     }
741
742     for (i = 1; i < argc; i++) {
743         const char *arg = argv[i];
744         unsigned long l;
745         char *end;
746
747         if (!strncmp(arg, "--bench", 7)) {
748             if (bench_init() < 0)
749                 return 1;
750             if (arg[7] == '=') {
751                 state.bench_pattern = arg + 8;
752                 state.bench_pattern_len = strlen(state.bench_pattern);
753             } else
754                 state.bench_pattern = "";
755         } else if (!strncmp(arg, "--test=", 7)) {
756             state.test_name = arg + 7;
757         } else if (!strcmp(arg, "--verbose") || !strcmp(arg, "-v")) {
758             state.verbose = 1;
759         } else if ((l = strtoul(arg, &end, 10)) <= UINT_MAX &&
760                    *end == '\0') {
761             seed = l;
762         } else {
763             return usage(argv[0]);
764         }
765     }
766
767     fprintf(stderr, "checkasm: using random seed %u\n", seed);
768     av_lfg_init(&checkasm_lfg, seed);
769
770     check_cpu_flag(NULL, 0);
771     for (i = 0; cpus[i].flag; i++)
772         check_cpu_flag(cpus[i].name, cpus[i].flag);
773
774     if (state.num_failed) {
775         fprintf(stderr, "checkasm: %d of %d tests have failed\n", state.num_failed, state.num_checked);
776         ret = 1;
777     } else {
778         fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
779         if (state.bench_pattern) {
780             print_benchs(state.funcs);
781         }
782     }
783
784     destroy_func_tree(state.funcs);
785     bench_uninit();
786     return ret;
787 }
788
789 /* Decide whether or not the specified function needs to be tested and
790  * allocate/initialize data structures if needed. Returns a pointer to a
791  * reference function if the function should be tested, otherwise NULL */
792 void *checkasm_check_func(void *func, const char *name, ...)
793 {
794     char name_buf[256];
795     void *ref = func;
796     CheckasmFuncVersion *v;
797     int name_length;
798     va_list arg;
799
800     va_start(arg, name);
801     name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
802     va_end(arg);
803
804     if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
805         return NULL;
806
807     state.current_func = get_func(&state.funcs, name_buf);
808     state.funcs->color = 1;
809     v = &state.current_func->versions;
810
811     if (v->func) {
812         CheckasmFuncVersion *prev;
813         do {
814             /* Only test functions that haven't already been tested */
815             if (v->func == func)
816                 return NULL;
817
818             if (v->ok)
819                 ref = v->func;
820
821             prev = v;
822         } while ((v = v->next));
823
824         v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
825     }
826
827     v->func = func;
828     v->ok = 1;
829     v->cpu = state.cpu_flag;
830     state.current_func_ver = v;
831
832     if (state.cpu_flag)
833         state.num_checked++;
834
835     return ref;
836 }
837
838 /* Decide whether or not the current function needs to be benchmarked */
839 int checkasm_bench_func(void)
840 {
841     return !state.num_failed && state.bench_pattern &&
842            !strncmp(state.current_func->name, state.bench_pattern, state.bench_pattern_len);
843 }
844
845 /* Indicate that the current test has failed */
846 void checkasm_fail_func(const char *msg, ...)
847 {
848     if (state.current_func_ver->cpu && state.current_func_ver->ok) {
849         va_list arg;
850
851         print_cpu_name();
852         fprintf(stderr, "   %s_%s (", state.current_func->name, cpu_suffix(state.current_func_ver->cpu));
853         va_start(arg, msg);
854         vfprintf(stderr, msg, arg);
855         va_end(arg);
856         fprintf(stderr, ")\n");
857
858         state.current_func_ver->ok = 0;
859         state.num_failed++;
860     }
861 }
862
863 /* Get the benchmark context of the current function */
864 CheckasmPerf *checkasm_get_perf_context(void)
865 {
866     CheckasmPerf *perf = &state.current_func_ver->perf;
867     memset(perf, 0, sizeof(*perf));
868     perf->sysfd = state.sysfd;
869     return perf;
870 }
871
872 /* Print the outcome of all tests performed since the last time this function was called */
873 void checkasm_report(const char *name, ...)
874 {
875     static int prev_checked, prev_failed, max_length;
876
877     if (state.num_checked > prev_checked) {
878         int pad_length = max_length + 4;
879         va_list arg;
880
881         print_cpu_name();
882         pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
883         va_start(arg, name);
884         pad_length -= vfprintf(stderr, name, arg);
885         va_end(arg);
886         fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
887
888         if (state.num_failed == prev_failed)
889             color_printf(COLOR_GREEN, "OK");
890         else
891             color_printf(COLOR_RED, "FAILED");
892         fprintf(stderr, "]\n");
893
894         prev_checked = state.num_checked;
895         prev_failed  = state.num_failed;
896     } else if (!state.cpu_flag) {
897         /* Calculate the amount of padding required to make the output vertically aligned */
898         int length = strlen(state.current_test_name);
899         va_list arg;
900
901         va_start(arg, name);
902         length += vsnprintf(NULL, 0, name, arg);
903         va_end(arg);
904
905         if (length > max_length)
906             max_length = length;
907     }
908 }
909
910 #define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
911 int checkasm_check_##type(const char *const file, const int line, \
912                           const type *buf1, ptrdiff_t stride1, \
913                           const type *buf2, ptrdiff_t stride2, \
914                           const int w, int h, const char *const name) \
915 { \
916     int y = 0; \
917     stride1 /= sizeof(*buf1); \
918     stride2 /= sizeof(*buf2); \
919     for (y = 0; y < h; y++) \
920         if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \
921             break; \
922     if (y == h) \
923         return 0; \
924     checkasm_fail_func("%s:%d", file, line); \
925     if (!state.verbose) \
926         return 1; \
927     fprintf(stderr, "%s:\n", name); \
928     while (h--) { \
929         for (int x = 0; x < w; x++) \
930             fprintf(stderr, " " fmt, buf1[x]); \
931         fprintf(stderr, "    "); \
932         for (int x = 0; x < w; x++) \
933             fprintf(stderr, " " fmt, buf2[x]); \
934         fprintf(stderr, "    "); \
935         for (int x = 0; x < w; x++) \
936             fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
937         buf1 += stride1; \
938         buf2 += stride2; \
939         fprintf(stderr, "\n"); \
940     } \
941     return 1; \
942 }
943
944 DEF_CHECKASM_CHECK_FUNC(uint8_t,  "%02x")
945 DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
946 DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x")
947 DEF_CHECKASM_CHECK_FUNC(int16_t,  "%6d")
948 DEF_CHECKASM_CHECK_FUNC(int32_t,  "%9d")