#include "checkasm.h"
#include "libavutil/common.h"
#include "libavutil/cpu.h"
+#include "libavutil/intfloat.h"
#include "libavutil/random_seed.h"
-#if ARCH_X86
-#include "libavutil/x86/cpu.h"
+#if HAVE_IO_H
+#include <io.h>
#endif
#if HAVE_SETCONSOLETEXTATTRIBUTE
#define isatty(fd) 1
#endif
+#if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
+#include "libavutil/arm/cpu.h"
+
+void (*checkasm_checked_call)(void *func, int dummy, ...) = checkasm_checked_call_novfp;
+#endif
+
/* List of tests to invoke */
-static void (* const tests[])(void) = {
+static const struct {
+ const char *name;
+ void (*func)(void);
+} tests[] = {
+#if CONFIG_BSWAPDSP
+ { "bswapdsp", checkasm_check_bswapdsp },
+#endif
+#if CONFIG_DCA_DECODER
+ { "dcadsp", checkasm_check_dcadsp },
+ { "synth_filter", checkasm_check_synth_filter },
+#endif
+#if CONFIG_FMTCONVERT
+ { "fmtconvert", checkasm_check_fmtconvert },
+#endif
+#if CONFIG_H264DSP
+ { "h264dsp", checkasm_check_h264dsp },
+#endif
#if CONFIG_H264PRED
- checkasm_check_h264pred,
+ { "h264pred", checkasm_check_h264pred },
+#endif
+#if CONFIG_H264QPEL
+ { "h264qpel", checkasm_check_h264qpel },
#endif
- NULL
+#if CONFIG_HEVC_DECODER
+ { "hevc_mc", checkasm_check_hevc_mc },
+#endif
+#if CONFIG_V210_ENCODER
+ { "v210enc", checkasm_check_v210enc },
+#endif
+#if CONFIG_VP8DSP
+ { "vp8dsp", checkasm_check_vp8dsp },
+#endif
+ { NULL }
};
/* List of cpu flags to check */
const char *suffix;
int flag;
} cpus[] = {
-#if ARCH_X86
+#if ARCH_AARCH64
+ { "ARMV8", "armv8", AV_CPU_FLAG_ARMV8 },
+ { "NEON", "neon", AV_CPU_FLAG_NEON },
+#elif ARCH_ARM
+ { "ARMV5TE", "armv5te", AV_CPU_FLAG_ARMV5TE },
+ { "ARMV6", "armv6", AV_CPU_FLAG_ARMV6 },
+ { "ARMV6T2", "armv6t2", AV_CPU_FLAG_ARMV6T2 },
+ { "VFP", "vfp", AV_CPU_FLAG_VFP },
+ { "VFP_VM", "vfp_vm", AV_CPU_FLAG_VFP_VM },
+ { "VFPV3", "vfp3", AV_CPU_FLAG_VFPV3 },
+ { "NEON", "neon", AV_CPU_FLAG_NEON },
+#elif ARCH_PPC
+ { "ALTIVEC", "altivec", AV_CPU_FLAG_ALTIVEC },
+ { "VSX", "vsx", AV_CPU_FLAG_VSX },
+ { "POWER8", "power8", AV_CPU_FLAG_POWER8 },
+#elif ARCH_X86
{ "MMX", "mmx", AV_CPU_FLAG_MMX|AV_CPU_FLAG_CMOV },
{ "MMXEXT", "mmxext", AV_CPU_FLAG_MMXEXT },
{ "3DNOW", "3dnow", AV_CPU_FLAG_3DNOW },
typedef struct CheckasmFuncVersion {
struct CheckasmFuncVersion *next;
- intptr_t (*func)();
+ void *func;
int ok;
int cpu;
int iterations;
typedef struct CheckasmFunc {
struct CheckasmFunc *child[2];
CheckasmFuncVersion versions;
+ uint8_t color; /* 0 = red, 1 = black */
char name[1];
} CheckasmFunc;
CheckasmFunc *funcs;
CheckasmFunc *current_func;
CheckasmFuncVersion *current_func_ver;
+ const char *current_test_name;
const char *bench_pattern;
int bench_pattern_len;
int num_checked;
/* PRNG state */
AVLFG checkasm_lfg;
+/* float compare support code */
+static int is_negative(union av_intfloat32 u)
+{
+ return u.i >> 31;
+}
+
+int float_near_ulp(float a, float b, unsigned max_ulp)
+{
+ union av_intfloat32 x, y;
+
+ x.f = a;
+ y.f = b;
+
+ if (is_negative(x) != is_negative(y)) {
+ // handle -0.0 == +0.0
+ return a == b;
+ }
+
+ if (abs(x.i - y.i) <= max_ulp)
+ return 1;
+
+ return 0;
+}
+
+int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
+ unsigned len)
+{
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ if (!float_near_ulp(a[i], b[i], max_ulp))
+ return 0;
+ }
+ return 1;
+}
+
+int float_near_abs_eps(float a, float b, float eps)
+{
+ float abs_diff = fabsf(a - b);
+
+ return abs_diff < eps;
+}
+
+int float_near_abs_eps_array(const float *a, const float *b, float eps,
+ unsigned len)
+{
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ if (!float_near_abs_eps(a[i], b[i], eps))
+ return 0;
+ }
+ return 1;
+}
+
+int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp)
+{
+ return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
+}
+
+int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
+ unsigned max_ulp, unsigned len)
+{
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
+ return 0;
+ }
+ return 1;
+}
+
/* Print colored text to stderr if the terminal supports it */
static void color_printf(int color, const char *fmt, ...)
{
/* ASCIIbetical sort except preserving natural order for numbers */
static int cmp_func_names(const char *a, const char *b)
{
+ const char *start = a;
int ascii_diff, digit_diff;
- for (; !(ascii_diff = *a - *b) && *a; a++, b++);
+ for (; !(ascii_diff = *(const unsigned char*)a - *(const unsigned char*)b) && *a; a++, b++);
for (; av_isdigit(*a) && av_isdigit(*b); a++, b++);
- return (digit_diff = av_isdigit(*a) - av_isdigit(*b)) ? digit_diff : ascii_diff;
+ if (a > start && av_isdigit(a[-1]) && (digit_diff = av_isdigit(*a) - av_isdigit(*b)))
+ return digit_diff;
+
+ return ascii_diff;
+}
+
+/* Perform a tree rotation in the specified direction and return the new root */
+static CheckasmFunc *rotate_tree(CheckasmFunc *f, int dir)
+{
+ CheckasmFunc *r = f->child[dir^1];
+ f->child[dir^1] = r->child[dir];
+ r->child[dir] = f;
+ r->color = f->color;
+ f->color = 0;
+ return r;
+}
+
+#define is_red(f) ((f) && !(f)->color)
+
+/* Balance a left-leaning red-black tree at the specified node */
+static void balance_tree(CheckasmFunc **root)
+{
+ CheckasmFunc *f = *root;
+
+ if (is_red(f->child[0]) && is_red(f->child[1])) {
+ f->color ^= 1;
+ f->child[0]->color = f->child[1]->color = 1;
+ }
+
+ if (!is_red(f->child[0]) && is_red(f->child[1]))
+ *root = rotate_tree(f, 0); /* Rotate left */
+ else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
+ *root = rotate_tree(f, 1); /* Rotate right */
}
/* Get a node with the specified name, creating it if it doesn't exist */
-static CheckasmFunc *get_func(const char *name, int length)
+static CheckasmFunc *get_func(CheckasmFunc **root, const char *name)
{
- CheckasmFunc *f, **f_ptr = &state.funcs;
+ CheckasmFunc *f = *root;
- /* Search the tree for a matching node */
- while ((f = *f_ptr)) {
+ if (f) {
+ /* Search the tree for a matching node */
int cmp = cmp_func_names(name, f->name);
- if (!cmp)
- return f;
+ if (cmp) {
+ f = get_func(&f->child[cmp > 0], name);
- f_ptr = &f->child[(cmp > 0)];
+ /* Rebalance the tree on the way up if a new node was inserted */
+ if (!f->versions.func)
+ balance_tree(root);
+ }
+ } else {
+ /* Allocate and insert a new node into the tree */
+ int name_length = strlen(name);
+ f = *root = checkasm_malloc(sizeof(CheckasmFunc) + name_length);
+ memcpy(f->name, name, name_length + 1);
}
- /* Allocate and insert a new node into the tree */
- f = *f_ptr = checkasm_malloc(sizeof(CheckasmFunc) + length);
- memcpy(f->name, name, length+1);
-
return f;
}
int i;
state.cpu_flag_name = name;
- for (i = 0; tests[i]; i++)
- tests[i]();
+ for (i = 0; tests[i].func; i++) {
+ state.current_test_name = tests[i].name;
+ tests[i].func();
+ }
}
}
{
int i, seed, ret = 0;
- if (!tests[0] || !cpus[0].flag) {
+#if ARCH_ARM && HAVE_ARMV5TE_EXTERNAL
+ if (have_vfp(av_get_cpu_flags()) || have_neon(av_get_cpu_flags()))
+ checkasm_checked_call = checkasm_checked_call_vfp;
+#endif
+
+ if (!tests[0].func || !cpus[0].flag) {
fprintf(stderr, "checkasm: no tests to perform\n");
- return 1;
+ return 0;
}
if (argc > 1 && !strncmp(argv[1], "--bench", 7)) {
/* Decide whether or not the specified function needs to be tested and
* allocate/initialize data structures if needed. Returns a pointer to a
* reference function if the function should be tested, otherwise NULL */
-intptr_t (*checkasm_check_func(intptr_t (*func)(), const char *name, ...))()
+void *checkasm_check_func(void *func, const char *name, ...)
{
char name_buf[256];
- intptr_t (*ref)() = func;
+ void *ref = func;
CheckasmFuncVersion *v;
int name_length;
va_list arg;
if (!func || name_length <= 0 || name_length >= sizeof(name_buf))
return NULL;
- state.current_func = get_func(name_buf, name_length);
+ state.current_func = get_func(&state.funcs, name_buf);
+ state.funcs->color = 1;
v = &state.current_func->versions;
if (v->func) {
static int prev_checked, prev_failed, max_length;
if (state.num_checked > prev_checked) {
- print_cpu_name();
-
- if (*name) {
- int pad_length = max_length;
- va_list arg;
+ int pad_length = max_length + 4;
+ va_list arg;
- fprintf(stderr, " - ");
- va_start(arg, name);
- pad_length -= vfprintf(stderr, name, arg);
- va_end(arg);
- fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
- } else
- fprintf(stderr, " - %-*s [", max_length, state.current_func->name);
+ print_cpu_name();
+ pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
+ va_start(arg, name);
+ pad_length -= vfprintf(stderr, name, arg);
+ va_end(arg);
+ fprintf(stderr, "%*c", FFMAX(pad_length, 0) + 2, '[');
if (state.num_failed == prev_failed)
color_printf(COLOR_GREEN, "OK");
prev_checked = state.num_checked;
prev_failed = state.num_failed;
} else if (!state.cpu_flag) {
- int length;
-
/* Calculate the amount of padding required to make the output vertically aligned */
- if (*name) {
- va_list arg;
- va_start(arg, name);
- length = vsnprintf(NULL, 0, name, arg);
- va_end(arg);
- } else
- length = strlen(state.current_func->name);
+ int length = strlen(state.current_test_name);
+ va_list arg;
+
+ va_start(arg, name);
+ length += vsnprintf(NULL, 0, name, arg);
+ va_end(arg);
if (length > max_length)
max_length = length;