]> git.sesse.net Git - ffmpeg/blobdiff - tests/checkasm/checkasm.c
mov: Wrap stsc index and count compare in a separate function
[ffmpeg] / tests / checkasm / checkasm.c
index 59383b8931746ca465df5cbf4cb7e35df9542cfc..739da6103650ce945beb13509234ff4041702c5a 100644 (file)
 #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 */
@@ -66,7 +101,22 @@ static const struct {
     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 },
@@ -88,7 +138,7 @@ static const struct {
 
 typedef struct CheckasmFuncVersion {
     struct CheckasmFuncVersion *next;
-    intptr_t (*func)();
+    void *func;
     int ok;
     int cpu;
     int iterations;
@@ -99,6 +149,7 @@ typedef struct CheckasmFuncVersion {
 typedef struct CheckasmFunc {
     struct CheckasmFunc *child[2];
     CheckasmFuncVersion versions;
+    uint8_t color; /* 0 = red, 1 = black */
     char name[1];
 } CheckasmFunc;
 
@@ -107,6 +158,7 @@ static struct {
     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;
@@ -119,6 +171,78 @@ static struct {
 /* 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, ...)
 {
@@ -252,32 +376,69 @@ static void print_benchs(CheckasmFunc *f)
 /* 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;
 }
 
@@ -294,8 +455,10 @@ static void check_cpu_flag(const char *name, int flag)
         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();
+        }
     }
 }
 
@@ -312,9 +475,14 @@ int main(int argc, char *argv[])
 {
     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)) {
@@ -361,10 +529,10 @@ int main(int argc, char *argv[])
 /* 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;
@@ -376,7 +544,8 @@ intptr_t (*checkasm_check_func(intptr_t (*func)(), const char *name, ...))()
     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) {
@@ -444,19 +613,15 @@ void checkasm_report(const char *name, ...)
     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");
@@ -467,16 +632,13 @@ void checkasm_report(const char *name, ...)
         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;