Commit | Line | Data |
---|---|---|
8bc67ec2 HG |
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 Libav. | |
7 | * | |
8 | * Libav 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 | * Libav 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 Libav; if not, write to the Free Software Foundation, Inc., | |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
22 | ||
7c82d31c DB |
23 | #ifndef TESTS_CHECKASM_CHECKASM_H |
24 | #define TESTS_CHECKASM_CHECKASM_H | |
8bc67ec2 HG |
25 | |
26 | #include <stdint.h> | |
27 | #include "config.h" | |
28 | #include "libavutil/avstring.h" | |
711781d7 | 29 | #include "libavutil/cpu.h" |
8bc67ec2 HG |
30 | #include "libavutil/lfg.h" |
31 | #include "libavutil/timer.h" | |
32 | ||
d37f2326 | 33 | void checkasm_check_bswapdsp(void); |
e71b747e | 34 | void checkasm_check_dcadsp(void); |
489e6add | 35 | void checkasm_check_fmtconvert(void); |
8bc67ec2 | 36 | void checkasm_check_h264pred(void); |
2cb34f82 | 37 | void checkasm_check_h264qpel(void); |
0cef06df | 38 | void checkasm_check_hevc_mc(void); |
568a4323 | 39 | void checkasm_check_synth_filter(void); |
3cdda78d | 40 | void checkasm_check_v210enc(void); |
8bc67ec2 | 41 | |
515b69f8 | 42 | void *checkasm_check_func(void *func, const char *name, ...) av_printf_format(2, 3); |
8bc67ec2 HG |
43 | int checkasm_bench_func(void); |
44 | void checkasm_fail_func(const char *msg, ...) av_printf_format(1, 2); | |
45 | void checkasm_update_bench(int iterations, uint64_t cycles); | |
46 | void checkasm_report(const char *name, ...) av_printf_format(1, 2); | |
47 | ||
9d218d57 JG |
48 | /* float compare utilities */ |
49 | int float_near_ulp(float a, float b, unsigned max_ulp); | |
50 | int float_near_abs_eps(float a, float b, float eps); | |
51 | int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp); | |
52 | int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp, | |
53 | unsigned len); | |
54 | int float_near_abs_eps_array(const float *a, const float *b, float eps, | |
55 | unsigned len); | |
56 | int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps, | |
57 | unsigned max_ulp, unsigned len); | |
58 | ||
8bc67ec2 HG |
59 | extern AVLFG checkasm_lfg; |
60 | #define rnd() av_lfg_get(&checkasm_lfg) | |
61 | ||
515b69f8 | 62 | static av_unused void *func_ref, *func_new; |
8bc67ec2 HG |
63 | |
64 | #define BENCH_RUNS 1000 /* Trade-off between accuracy and speed */ | |
65 | ||
66 | /* Decide whether or not the specified function needs to be tested */ | |
515b69f8 HG |
67 | #define check_func(func, ...) (func_ref = checkasm_check_func((func_new = func), __VA_ARGS__)) |
68 | ||
69 | /* Declare the function prototype. The first argument is the return value, the remaining | |
70 | * arguments are the function parameters. Naming parameters is optional. */ | |
71 | #define declare_func(ret, ...) declare_new(ret, __VA_ARGS__) typedef ret func_type(__VA_ARGS__) | |
711781d7 | 72 | #define declare_func_emms(cpu_flags, ret, ...) declare_new_emms(cpu_flags, ret, __VA_ARGS__) typedef ret func_type(__VA_ARGS__) |
8bc67ec2 HG |
73 | |
74 | /* Indicate that the current test has failed */ | |
75 | #define fail() checkasm_fail_func("%s:%d", av_basename(__FILE__), __LINE__) | |
76 | ||
77 | /* Print the test outcome */ | |
65c14801 | 78 | #define report checkasm_report |
8bc67ec2 HG |
79 | |
80 | /* Call the reference function */ | |
515b69f8 | 81 | #define call_ref(...) ((func_type *)func_ref)(__VA_ARGS__) |
8bc67ec2 HG |
82 | |
83 | #if ARCH_X86 && HAVE_YASM | |
711781d7 JG |
84 | /* Verifies that clobbered callee-saved registers are properly saved and restored |
85 | * and that either no MMX registers are touched or emms is issued */ | |
515b69f8 | 86 | void checkasm_checked_call(void *func, ...); |
711781d7 JG |
87 | /* Verifies that clobbered callee-saved registers are properly saved and restored |
88 | * and issues emms for asm functions which are not required to do so */ | |
89 | void checkasm_checked_call_emms(void *func, ...); | |
8bc67ec2 | 90 | |
515b69f8 | 91 | #if ARCH_X86_64 |
8bc67ec2 HG |
92 | /* Evil hack: detect incorrect assumptions that 32-bit ints are zero-extended to 64-bit. |
93 | * This is done by clobbering the stack with junk around the stack pointer and calling the | |
515b69f8 | 94 | * assembly function through checked_call() with added dummy arguments which forces all |
8bc67ec2 HG |
95 | * real arguments to be passed on the stack and not in registers. For 32-bit arguments the |
96 | * upper half of the 64-bit register locations on the stack will now contain junk which will | |
97 | * cause misbehaving functions to either produce incorrect output or segfault. Note that | |
98 | * even though this works extremely well in practice, it's technically not guaranteed | |
99 | * and false negatives is theoretically possible, but there can never be any false positives. | |
100 | */ | |
101 | void checkasm_stack_clobber(uint64_t clobber, ...); | |
515b69f8 HG |
102 | #define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, int, __VA_ARGS__)\ |
103 | = (void *)checkasm_checked_call; | |
711781d7 JG |
104 | #define declare_new_emms(cpu_flags, ret, ...) \ |
105 | ret (*checked_call)(void *, int, int, int, int, int, __VA_ARGS__) = \ | |
106 | ((cpu_flags) & av_get_cpu_flags()) ? (void *)checkasm_checked_call_emms : \ | |
107 | (void *)checkasm_checked_call; | |
8bc67ec2 HG |
108 | #define CLOB (UINT64_C(0xdeadbeefdeadbeef)) |
109 | #define call_new(...) (checkasm_stack_clobber(CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,\ | |
110 | CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB),\ | |
515b69f8 HG |
111 | checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__)) |
112 | #elif ARCH_X86_32 | |
113 | #define declare_new(ret, ...) ret (*checked_call)(void *, __VA_ARGS__) = (void *)checkasm_checked_call; | |
711781d7 JG |
114 | #define declare_new_emms(cpu_flags, ret, ...) ret (*checked_call)(void *, __VA_ARGS__) = \ |
115 | ((cpu_flags) & av_get_cpu_flags()) ? (void *)checkasm_checked_call_emms : \ | |
116 | (void *)checkasm_checked_call; | |
515b69f8 HG |
117 | #define call_new(...) checked_call(func_new, __VA_ARGS__) |
118 | #endif | |
26ec75ae MS |
119 | #elif ARCH_ARM && HAVE_ARMV5TE_EXTERNAL |
120 | /* Use a dummy argument, to offset the real parameters by 2, not only 1. | |
121 | * This makes sure that potential 8-byte-alignment of parameters is kept the same | |
122 | * even when the extra parameters have been removed. */ | |
123 | void checkasm_checked_call_vfp(void *func, int dummy, ...); | |
124 | void checkasm_checked_call_novfp(void *func, int dummy, ...); | |
125 | extern void (*checkasm_checked_call)(void *func, int dummy, ...); | |
126 | #define declare_new(ret, ...) ret (*checked_call)(void *, int dummy, __VA_ARGS__) = (void *)checkasm_checked_call; | |
127 | #define call_new(...) checked_call(func_new, 0, __VA_ARGS__) | |
fec76cd4 MS |
128 | #elif ARCH_AARCH64 && !defined(__APPLE__) |
129 | void checkasm_checked_call(void *func, ...); | |
130 | #define declare_new(ret, ...) ret (*checked_call)(void *, __VA_ARGS__) = (void *)checkasm_checked_call; | |
131 | #define call_new(...) checked_call(func_new, __VA_ARGS__) | |
8bc67ec2 | 132 | #else |
515b69f8 | 133 | #define declare_new(ret, ...) |
711781d7 | 134 | #define declare_new_emms(cpu_flags, ret, ...) |
515b69f8 HG |
135 | /* Call the function */ |
136 | #define call_new(...) ((func_type *)func_new)(__VA_ARGS__) | |
8bc67ec2 HG |
137 | #endif |
138 | ||
26ec75ae MS |
139 | #ifndef declare_new_emms |
140 | #define declare_new_emms(cpu_flags, ret, ...) declare_new(ret, __VA_ARGS__) | |
141 | #endif | |
142 | ||
8bc67ec2 HG |
143 | /* Benchmark the function */ |
144 | #ifdef AV_READ_TIME | |
145 | #define bench_new(...)\ | |
146 | do {\ | |
147 | if (checkasm_bench_func()) {\ | |
515b69f8 | 148 | func_type *tfunc = func_new;\ |
8bc67ec2 HG |
149 | uint64_t tsum = 0;\ |
150 | int ti, tcount = 0;\ | |
151 | for (ti = 0; ti < BENCH_RUNS; ti++) {\ | |
152 | uint64_t t = AV_READ_TIME();\ | |
153 | tfunc(__VA_ARGS__);\ | |
154 | tfunc(__VA_ARGS__);\ | |
155 | tfunc(__VA_ARGS__);\ | |
156 | tfunc(__VA_ARGS__);\ | |
157 | t = AV_READ_TIME() - t;\ | |
158 | if (t*tcount <= tsum*4 && ti > 0) {\ | |
159 | tsum += t;\ | |
160 | tcount++;\ | |
161 | }\ | |
162 | }\ | |
163 | checkasm_update_bench(tcount, tsum);\ | |
164 | }\ | |
165 | } while (0) | |
166 | #else | |
cb33f8d0 | 167 | #define bench_new(...) while(0) |
8bc67ec2 HG |
168 | #endif |
169 | ||
7c82d31c | 170 | #endif /* TESTS_CHECKASM_CHECKASM_H */ |