summaryrefslogtreecommitdiffstats
path: root/third_party/dav1d/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/dav1d/tests/checkasm/arm/checkasm_32.S201
-rw-r--r--third_party/dav1d/tests/checkasm/arm/checkasm_64.S211
-rw-r--r--third_party/dav1d/tests/checkasm/cdef.c144
-rw-r--r--third_party/dav1d/tests/checkasm/checkasm.c986
-rw-r--r--third_party/dav1d/tests/checkasm/checkasm.h379
-rw-r--r--third_party/dav1d/tests/checkasm/filmgrain.c401
-rw-r--r--third_party/dav1d/tests/checkasm/ipred.c297
-rw-r--r--third_party/dav1d/tests/checkasm/itx.c318
-rw-r--r--third_party/dav1d/tests/checkasm/loopfilter.c203
-rw-r--r--third_party/dav1d/tests/checkasm/looprestoration.c196
-rw-r--r--third_party/dav1d/tests/checkasm/mc.c790
-rw-r--r--third_party/dav1d/tests/checkasm/msac.c294
-rw-r--r--third_party/dav1d/tests/checkasm/refmvs.c167
-rw-r--r--third_party/dav1d/tests/checkasm/x86/checkasm.asm475
-rwxr-xr-xthird_party/dav1d/tests/dav1d_argon.bash175
-rw-r--r--third_party/dav1d/tests/header_test.c33
-rw-r--r--third_party/dav1d/tests/libfuzzer/alloc_fail.c102
-rw-r--r--third_party/dav1d/tests/libfuzzer/alloc_fail.h35
-rw-r--r--third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c199
-rw-r--r--third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h37
-rw-r--r--third_party/dav1d/tests/libfuzzer/main.c100
-rw-r--r--third_party/dav1d/tests/libfuzzer/meson.build101
-rw-r--r--third_party/dav1d/tests/meson.build152
-rw-r--r--third_party/dav1d/tests/seek_stress.c243
24 files changed, 6239 insertions, 0 deletions
diff --git a/third_party/dav1d/tests/checkasm/arm/checkasm_32.S b/third_party/dav1d/tests/checkasm/arm/checkasm_32.S
new file mode 100644
index 0000000000..a186ef8fc2
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/arm/checkasm_32.S
@@ -0,0 +1,201 @@
+/******************************************************************************
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2015 Martin Storsjo
+ * Copyright © 2015 Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#define PRIVATE_PREFIX checkasm_
+
+#include "src/arm/asm.S"
+#include "src/arm/32/util.S"
+
+const register_init, align=3
+ .quad 0x21f86d66c8ca00ce
+ .quad 0x75b6ba21077c48ad
+ .quad 0xed56bb2dcb3c7736
+ .quad 0x8bda43d3fd1a7e06
+ .quad 0xb64a9c9e5d318408
+ .quad 0xdf9a54b303f1d3a3
+ .quad 0x4a75479abd64e097
+ .quad 0x249214109d5d1c88
+endconst
+
+const error_message_fpscr
+ .asciz "failed to preserve register FPSCR, changed bits: %x"
+error_message_gpr:
+ .asciz "failed to preserve register r%d"
+error_message_vfp:
+ .asciz "failed to preserve register d%d"
+error_message_stack:
+ .asciz "failed to preserve stack"
+endconst
+
+@ max number of args used by any asm function.
+#define MAX_ARGS 15
+
+#define ARG_STACK 4*(MAX_ARGS - 4)
+
+@ Align the used stack space to 8 to preserve the stack alignment.
+@ +8 for stack canary reference.
+#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed + 8)
+
+.macro clobbercheck variant
+.equ pushed, 4*9
+function checked_call_\variant, export=1
+ push {r4-r11, lr}
+.ifc \variant, vfp
+ vpush {d8-d15}
+ fmrx r4, FPSCR
+ push {r4}
+.equ pushed, pushed + 16*4 + 4
+.endif
+
+ movrel r12, register_init
+.ifc \variant, vfp
+ vldm r12, {d8-d15}
+.endif
+ ldm r12, {r4-r11}
+
+ sub sp, sp, #ARG_STACK_A
+.equ pos, 0
+.rept MAX_ARGS-4
+ ldr r12, [sp, #ARG_STACK_A + pushed + 8 + pos]
+ str r12, [sp, #pos]
+.equ pos, pos + 4
+.endr
+
+ @ For stack overflows, the callee is free to overwrite the parameters
+ @ that were passed on the stack (if any), so we can only check after
+ @ that point. First figure out how many parameters the function
+ @ really took on the stack:
+ ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
+ @ Load the first non-parameter value from the stack, that should be
+ @ left untouched by the function. Store a copy of it inverted, so that
+ @ e.g. overwriting everything with zero would be noticed.
+ ldr r12, [sp, r12, lsl #2]
+ mvn r12, r12
+ str r12, [sp, #ARG_STACK_A - 4]
+
+ mov r12, r0
+ mov r0, r2
+ mov r1, r3
+ ldrd r2, r3, [sp, #ARG_STACK_A + pushed]
+ @ Call the target function
+ blx r12
+
+ @ Load the number of stack parameters, stack canary and its reference
+ ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
+ ldr r2, [sp, r12, lsl #2]
+ ldr r3, [sp, #ARG_STACK_A - 4]
+
+ add sp, sp, #ARG_STACK_A
+ push {r0, r1}
+
+ mvn r3, r3
+ cmp r2, r3
+ bne 5f
+
+ movrel r12, register_init
+.ifc \variant, vfp
+.macro check_reg_vfp, dreg, offset
+ ldrd r2, r3, [r12, #8 * (\offset)]
+ vmov r0, lr, \dreg
+ eor r2, r2, r0
+ eor r3, r3, lr
+ orrs r2, r2, r3
+ bne 4f
+.endm
+
+.irp n, 8, 9, 10, 11, 12, 13, 14, 15
+ @ keep track of the checked double/SIMD register
+ mov r1, #\n
+ check_reg_vfp d\n, \n-8
+.endr
+.purgem check_reg_vfp
+
+ fmrx r1, FPSCR
+ ldr r3, [sp, #8]
+ eor r1, r1, r3
+ @ Ignore changes in bits 0-4 and 7
+ bic r1, r1, #0x9f
+ @ Ignore changes in the topmost 5 bits
+ bics r1, r1, #0xf8000000
+ bne 3f
+.endif
+
+ @ keep track of the checked GPR
+ mov r1, #4
+.macro check_reg reg1, reg2=
+ ldrd r2, r3, [r12], #8
+ eors r2, r2, \reg1
+ bne 2f
+ add r1, r1, #1
+.ifnb \reg2
+ eors r3, r3, \reg2
+ bne 2f
+.endif
+ add r1, r1, #1
+.endm
+ check_reg r4, r5
+ check_reg r6, r7
+@ r9 is a volatile register in the ios ABI
+#ifdef __APPLE__
+ check_reg r8
+#else
+ check_reg r8, r9
+#endif
+ check_reg r10, r11
+.purgem check_reg
+
+ b 0f
+5:
+ movrel r0, error_message_stack
+ b 1f
+4:
+ movrel r0, error_message_vfp
+ b 1f
+3:
+ movrel r0, error_message_fpscr
+ b 1f
+2:
+ movrel r0, error_message_gpr
+1:
+#ifdef PREFIX
+ bl _checkasm_fail_func
+#else
+ bl checkasm_fail_func
+#endif
+0:
+ pop {r0, r1}
+.ifc \variant, vfp
+ pop {r2}
+ fmxr FPSCR, r2
+ vpop {d8-d15}
+.endif
+ pop {r4-r11, pc}
+endfunc
+.endm
+
+clobbercheck vfp
diff --git a/third_party/dav1d/tests/checkasm/arm/checkasm_64.S b/third_party/dav1d/tests/checkasm/arm/checkasm_64.S
new file mode 100644
index 0000000000..25749145a5
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/arm/checkasm_64.S
@@ -0,0 +1,211 @@
+/******************************************************************************
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2015 Martin Storsjo
+ * Copyright © 2015 Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#define PRIVATE_PREFIX checkasm_
+
+#include "src/arm/asm.S"
+#include "src/arm/64/util.S"
+
+const register_init, align=4
+ .quad 0x21f86d66c8ca00ce
+ .quad 0x75b6ba21077c48ad
+ .quad 0xed56bb2dcb3c7736
+ .quad 0x8bda43d3fd1a7e06
+ .quad 0xb64a9c9e5d318408
+ .quad 0xdf9a54b303f1d3a3
+ .quad 0x4a75479abd64e097
+ .quad 0x249214109d5d1c88
+ .quad 0x1a1b2550a612b48c
+ .quad 0x79445c159ce79064
+ .quad 0x2eed899d5a28ddcd
+ .quad 0x86b2536fcd8cf636
+ .quad 0xb0856806085e7943
+ .quad 0x3f2bf84fc0fcca4e
+ .quad 0xacbd382dcf5b8de2
+ .quad 0xd229e1f5b281303f
+ .quad 0x71aeaff20b095fd9
+ .quad 0xab63e2e11fa38ed9
+endconst
+
+
+const error_message_register
+ .asciz "failed to preserve register"
+error_message_stack:
+ .asciz "stack clobbered"
+endconst
+
+
+// max number of args used by any asm function.
+#define MAX_ARGS 15
+
+#define CLOBBER_STACK ((8*MAX_ARGS + 15) & ~15)
+
+function stack_clobber, export=1
+ mov x3, sp
+ mov x2, #CLOBBER_STACK
+1:
+ stp x0, x1, [sp, #-16]!
+ subs x2, x2, #16
+ b.gt 1b
+ mov sp, x3
+ ret
+endfunc
+
+// + 16 for stack canary reference
+#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15 + 16)
+
+function checked_call, export=1
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ stp x19, x20, [sp, #-16]!
+ stp x21, x22, [sp, #-16]!
+ stp x23, x24, [sp, #-16]!
+ stp x25, x26, [sp, #-16]!
+ stp x27, x28, [sp, #-16]!
+ stp d8, d9, [sp, #-16]!
+ stp d10, d11, [sp, #-16]!
+ stp d12, d13, [sp, #-16]!
+ stp d14, d15, [sp, #-16]!
+
+ movrel x9, register_init
+ ldp d8, d9, [x9], #16
+ ldp d10, d11, [x9], #16
+ ldp d12, d13, [x9], #16
+ ldp d14, d15, [x9], #16
+ ldp x19, x20, [x9], #16
+ ldp x21, x22, [x9], #16
+ ldp x23, x24, [x9], #16
+ ldp x25, x26, [x9], #16
+ ldp x27, x28, [x9], #16
+
+ sub sp, sp, #ARG_STACK
+.equ pos, 0
+.rept MAX_ARGS-8
+ // Skip the first 8 args, that are loaded into registers
+ ldr x9, [x29, #16 + 8*8 + pos]
+ str x9, [sp, #pos]
+.equ pos, pos + 8
+.endr
+
+ // Fill x8-x17 with garbage. This doesn't have to be preserved,
+ // but avoids relying on them having any particular value.
+ movrel x9, register_init
+ ldp x10, x11, [x9], #32
+ ldp x12, x13, [x9], #32
+ ldp x14, x15, [x9], #32
+ ldp x16, x17, [x9], #32
+ ldp x8, x9, [x9]
+
+ // For stack overflows, the callee is free to overwrite the parameters
+ // that were passed on the stack (if any), so we can only check after
+ // that point. First figure out how many parameters the function
+ // really took on the stack:
+ ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8]
+ // Load the first non-parameter value from the stack, that should be
+ // left untouched by the function. Store a copy of it inverted, so that
+ // e.g. overwriting everything with zero would be noticed.
+ ldr x2, [sp, x2, lsl #3]
+ mvn x2, x2
+ str x2, [sp, #ARG_STACK-8]
+
+ // Load the in-register arguments
+ mov x12, x0
+ ldp x0, x1, [x29, #16]
+ ldp x2, x3, [x29, #32]
+ ldp x4, x5, [x29, #48]
+ ldp x6, x7, [x29, #64]
+ // Call the target function
+ blr x12
+
+ // Load the number of stack parameters, stack canary and its reference
+ ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8]
+ ldr x2, [sp, x2, lsl #3]
+ ldr x3, [sp, #ARG_STACK-8]
+
+ add sp, sp, #ARG_STACK
+ stp x0, x1, [sp, #-16]!
+
+ mvn x3, x3
+ cmp x2, x3
+ b.ne 2f
+
+ movrel x9, register_init
+ movi v3.8h, #0
+
+.macro check_reg_neon reg1, reg2
+ ldr q1, [x9], #16
+ uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d
+ eor v1.16b, v1.16b, v2.16b
+ orr v3.16b, v3.16b, v1.16b
+.endm
+ check_reg_neon 8, 9
+ check_reg_neon 10, 11
+ check_reg_neon 12, 13
+ check_reg_neon 14, 15
+ uqxtn v3.8b, v3.8h
+ umov x3, v3.d[0]
+
+.macro check_reg reg1, reg2
+ ldp x0, x1, [x9], #16
+ eor x0, x0, \reg1
+ eor x1, x1, \reg2
+ orr x3, x3, x0
+ orr x3, x3, x1
+.endm
+ check_reg x19, x20
+ check_reg x21, x22
+ check_reg x23, x24
+ check_reg x25, x26
+ check_reg x27, x28
+
+ cbz x3, 0f
+
+ movrel x0, error_message_register
+ b 1f
+2:
+ movrel x0, error_message_stack
+1:
+#ifdef PREFIX
+ bl _checkasm_fail_func
+#else
+ bl checkasm_fail_func
+#endif
+0:
+ ldp x0, x1, [sp], #16
+ ldp d14, d15, [sp], #16
+ ldp d12, d13, [sp], #16
+ ldp d10, d11, [sp], #16
+ ldp d8, d9, [sp], #16
+ ldp x27, x28, [sp], #16
+ ldp x25, x26, [sp], #16
+ ldp x23, x24, [sp], #16
+ ldp x21, x22, [sp], #16
+ ldp x19, x20, [sp], #16
+ ldp x29, x30, [sp], #16
+ ret
+endfunc
diff --git a/third_party/dav1d/tests/checkasm/cdef.c b/third_party/dav1d/tests/checkasm/cdef.c
new file mode 100644
index 0000000000..9a90e313da
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/cdef.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "common/dump.h"
+
+#include "src/levels.h"
+#include "src/cdef.h"
+
+static int to_binary(int x) { /* 0-15 -> 0000-1111 */
+ return (x & 1) + 5 * (x & 2) + 25 * (x & 4) + 125 * (x & 8);
+}
+
+static void init_tmp(pixel *buf, int n, const int bitdepth_max) {
+ const int fill_type = rnd() & 7;
+ if (fill_type == 0)
+ while (n--) /* check for cdef_filter underflows */
+ *buf++ = rnd() & 1;
+ else if (fill_type == 1)
+ while (n--) /* check for cdef_filter overflows */
+ *buf++ = bitdepth_max - (rnd() & 1);
+ else
+ while (n--)
+ *buf++ = rnd() & bitdepth_max;
+}
+
+static void check_cdef_filter(const cdef_fn fn, const int w, const int h) {
+ ALIGN_STK_64(pixel, c_src, 16 * 10 + 16, ), *const c_dst = c_src + 8;
+ ALIGN_STK_64(pixel, a_src, 16 * 10 + 16, ), *const a_dst = a_src + 8;
+ ALIGN_STK_64(pixel, top_buf, 16 * 2 + 16, ), *const top = top_buf + 8;
+ ALIGN_STK_64(pixel, bot_buf, 16 * 2 + 16, ), *const bot = bot_buf + 8;
+ ALIGN_STK_16(pixel, left, 8,[2]);
+ const ptrdiff_t stride = 16 * sizeof(pixel);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel (*left)[2],
+ const pixel *top, const pixel *bot, int pri_strength,
+ int sec_strength, int dir, int damping,
+ enum CdefEdgeFlags edges HIGHBD_DECL_SUFFIX);
+
+ for (int s = 0x1; s <= 0x3; s++) {
+ if (check_func(fn, "cdef_filter_%dx%d_%02d_%dbpc", w, h, to_binary(s), BITDEPTH)) {
+ for (int dir = 0; dir < 8; dir++) {
+ for (enum CdefEdgeFlags edges = 0x0; edges <= 0xf; edges++) {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8;
+
+ init_tmp(c_src, 16 * 10 + 16, bitdepth_max);
+ init_tmp(top_buf, 16 * 2 + 16, bitdepth_max);
+ init_tmp(bot_buf, 16 * 2 + 16, bitdepth_max);
+ init_tmp((pixel *) left, 8 * 2, bitdepth_max);
+ memcpy(a_src, c_src, (16 * 10 + 16) * sizeof(pixel));
+
+ const int pri_strength = s & 2 ? (1 + (rnd() % 15)) << bitdepth_min_8 : 0;
+ const int sec_strength = s & 1 ? 1 << ((rnd() % 3) + bitdepth_min_8) : 0;
+ const int damping = 3 + (rnd() & 3) + bitdepth_min_8 - (w == 4 || (rnd() & 1));
+ call_ref(c_dst, stride, left, top, bot, pri_strength, sec_strength,
+ dir, damping, edges HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, stride, left, top, bot, pri_strength, sec_strength,
+ dir, damping, edges HIGHBD_TAIL_SUFFIX);
+ if (checkasm_check_pixel(c_dst, stride, a_dst, stride, w, h, "dst")) {
+ fprintf(stderr, "strength = %d:%d, dir = %d, damping = %d, edges = %04d\n",
+ pri_strength, sec_strength, dir, damping, to_binary(edges));
+ return;
+ }
+ if (dir == 7 && (edges == 0x5 || edges == 0xa || edges == 0xf))
+ bench_new(alternate(c_dst, a_dst), stride, left, top, bot, pri_strength,
+ sec_strength, dir, damping, edges HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+ }
+}
+
+static void check_cdef_direction(const cdef_dir_fn fn) {
+ ALIGN_STK_64(pixel, src, 8 * 8,);
+
+ declare_func(int, pixel *src, ptrdiff_t dst_stride, unsigned *var
+ HIGHBD_DECL_SUFFIX);
+
+ if (check_func(fn, "cdef_dir_%dbpc", BITDEPTH)) {
+ unsigned c_var, a_var;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ init_tmp(src, 64, bitdepth_max);
+
+ const int c_dir = call_ref(src, 8 * sizeof(pixel), &c_var HIGHBD_TAIL_SUFFIX);
+ const int a_dir = call_new(src, 8 * sizeof(pixel), &a_var HIGHBD_TAIL_SUFFIX);
+ if (c_var != a_var || c_dir != a_dir) {
+ if (fail()) {
+ hex_fdump(stderr, src, 8 * sizeof(pixel), 8, 8, "src");
+ fprintf(stderr, "c_dir %d a_dir %d\n", c_dir, a_dir);
+ }
+ }
+ bench_new(src, 8 * sizeof(pixel), &a_var HIGHBD_TAIL_SUFFIX);
+ }
+ report("cdef_dir");
+}
+
+void bitfn(checkasm_check_cdef)(void) {
+ Dav1dCdefDSPContext c;
+ bitfn(dav1d_cdef_dsp_init)(&c);
+
+ check_cdef_direction(c.dir);
+
+ check_cdef_filter(c.fb[0], 8, 8);
+ check_cdef_filter(c.fb[1], 4, 8);
+ check_cdef_filter(c.fb[2], 4, 4);
+ report("cdef_filter");
+}
diff --git a/third_party/dav1d/tests/checkasm/checkasm.c b/third_party/dav1d/tests/checkasm/checkasm.c
new file mode 100644
index 0000000000..bca2158b5b
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/checkasm.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "tests/checkasm/checkasm.h"
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/cpu.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#define COLOR_RED FOREGROUND_RED
+#define COLOR_GREEN FOREGROUND_GREEN
+#define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
+#else
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
+#define COLOR_RED 1
+#define COLOR_GREEN 2
+#define COLOR_YELLOW 3
+#endif
+
+/* List of tests to invoke */
+static const struct {
+ const char *name;
+ void (*func)(void);
+} tests[] = {
+ { "msac", checkasm_check_msac },
+ { "refmvs", checkasm_check_refmvs },
+#if CONFIG_8BPC
+ { "cdef_8bpc", checkasm_check_cdef_8bpc },
+ { "filmgrain_8bpc", checkasm_check_filmgrain_8bpc },
+ { "ipred_8bpc", checkasm_check_ipred_8bpc },
+ { "itx_8bpc", checkasm_check_itx_8bpc },
+ { "loopfilter_8bpc", checkasm_check_loopfilter_8bpc },
+ { "looprestoration_8bpc", checkasm_check_looprestoration_8bpc },
+ { "mc_8bpc", checkasm_check_mc_8bpc },
+#endif
+#if CONFIG_16BPC
+ { "cdef_16bpc", checkasm_check_cdef_16bpc },
+ { "filmgrain_16bpc", checkasm_check_filmgrain_16bpc },
+ { "ipred_16bpc", checkasm_check_ipred_16bpc },
+ { "itx_16bpc", checkasm_check_itx_16bpc },
+ { "loopfilter_16bpc", checkasm_check_loopfilter_16bpc },
+ { "looprestoration_16bpc", checkasm_check_looprestoration_16bpc },
+ { "mc_16bpc", checkasm_check_mc_16bpc },
+#endif
+ { 0 }
+};
+
+/* List of cpu flags to check */
+static const struct {
+ const char *name;
+ const char *suffix;
+ unsigned flag;
+} cpus[] = {
+#if ARCH_X86
+ { "SSE2", "sse2", DAV1D_X86_CPU_FLAG_SSE2 },
+ { "SSSE3", "ssse3", DAV1D_X86_CPU_FLAG_SSSE3 },
+ { "SSE4.1", "sse4", DAV1D_X86_CPU_FLAG_SSE41 },
+ { "AVX2", "avx2", DAV1D_X86_CPU_FLAG_AVX2 },
+ { "AVX-512 (Ice Lake)", "avx512icl", DAV1D_X86_CPU_FLAG_AVX512ICL },
+#elif ARCH_AARCH64 || ARCH_ARM
+ { "NEON", "neon", DAV1D_ARM_CPU_FLAG_NEON },
+#elif ARCH_PPC64LE
+ { "VSX", "vsx", DAV1D_PPC_CPU_FLAG_VSX },
+#endif
+ { 0 }
+};
+
+typedef struct CheckasmFuncVersion {
+ struct CheckasmFuncVersion *next;
+ void *func;
+ int ok;
+ unsigned cpu;
+ int iterations;
+ uint64_t cycles;
+} CheckasmFuncVersion;
+
+/* Binary search tree node */
+typedef struct CheckasmFunc {
+ struct CheckasmFunc *child[2];
+ CheckasmFuncVersion versions;
+ uint8_t color; /* 0 = red, 1 = black */
+ char name[];
+} CheckasmFunc;
+
+/* Internal state */
+static struct {
+ CheckasmFunc *funcs;
+ CheckasmFunc *current_func;
+ CheckasmFuncVersion *current_func_ver;
+ const char *current_test_name;
+ int num_checked;
+ int num_failed;
+ double nop_time;
+ unsigned cpu_flag;
+ const char *cpu_flag_name;
+ const char *test_pattern;
+ const char *function_pattern;
+ unsigned seed;
+ int bench;
+ int bench_c;
+ int verbose;
+ int function_listing;
+ int catch_signals;
+ int suffix_length;
+ int max_function_name_length;
+#if ARCH_X86_64
+ void (*simd_warmup)(void);
+#endif
+} state;
+
+/* float compare support code */
+typedef union {
+ float f;
+ uint32_t i;
+} intfloat;
+
+static uint32_t xs_state[4];
+
+static void xor128_srand(unsigned seed) {
+ xs_state[0] = seed;
+ xs_state[1] = ( seed & 0xffff0000) | (~seed & 0x0000ffff);
+ xs_state[2] = (~seed & 0xffff0000) | ( seed & 0x0000ffff);
+ xs_state[3] = ~seed;
+}
+
+// xor128 from Marsaglia, George (July 2003). "Xorshift RNGs".
+// Journal of Statistical Software. 8 (14).
+// doi:10.18637/jss.v008.i14.
+int xor128_rand(void) {
+ const uint32_t x = xs_state[0];
+ const uint32_t t = x ^ (x << 11);
+
+ xs_state[0] = xs_state[1];
+ xs_state[1] = xs_state[2];
+ xs_state[2] = xs_state[3];
+ uint32_t w = xs_state[3];
+
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+ xs_state[3] = w;
+
+ return w >> 1;
+}
+
+static int is_negative(const intfloat u) {
+ return u.i >> 31;
+}
+
+int float_near_ulp(const float a, const float b, const unsigned max_ulp) {
+ intfloat x, y;
+
+ x.f = a;
+ y.f = b;
+
+ if (is_negative(x) != is_negative(y)) {
+ // handle -0.0 == +0.0
+ return a == b;
+ }
+
+ if (llabs((int64_t)x.i - y.i) <= max_ulp)
+ return 1;
+
+ return 0;
+}
+
+int float_near_ulp_array(const float *const a, const float *const b,
+ const unsigned max_ulp, const int len)
+{
+ for (int i = 0; i < len; i++)
+ if (!float_near_ulp(a[i], b[i], max_ulp))
+ return 0;
+
+ return 1;
+}
+
+int float_near_abs_eps(const float a, const float b, const float eps) {
+ return fabsf(a - b) < eps;
+}
+
+int float_near_abs_eps_array(const float *const a, const float *const b,
+ const float eps, const int len)
+{
+ for (int 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(const float a, const float b, const float eps,
+ const 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 *const a, const float *const b,
+ const float eps, const unsigned max_ulp,
+ const int len)
+{
+ for (int 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(const int color, const char *const fmt, ...) {
+ static int8_t use_color = -1;
+ va_list arg;
+
+#ifdef _WIN32
+ static HANDLE con;
+ static WORD org_attributes;
+
+ if (use_color < 0) {
+ CONSOLE_SCREEN_BUFFER_INFO con_info;
+ con = GetStdHandle(STD_ERROR_HANDLE);
+ if (con && con != INVALID_HANDLE_VALUE &&
+ GetConsoleScreenBufferInfo(con, &con_info))
+ {
+ org_attributes = con_info.wAttributes;
+ use_color = 1;
+ } else
+ use_color = 0;
+ }
+ if (use_color)
+ SetConsoleTextAttribute(con, (org_attributes & 0xfff0) |
+ (color & 0x0f));
+#else
+ if (use_color < 0) {
+ const char *const term = getenv("TERM");
+ use_color = term && strcmp(term, "dumb") && isatty(2);
+ }
+ if (use_color)
+ fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
+#endif
+
+ va_start(arg, fmt);
+ vfprintf(stderr, fmt, arg);
+ va_end(arg);
+
+ if (use_color) {
+#ifdef _WIN32
+ SetConsoleTextAttribute(con, org_attributes);
+#else
+ fprintf(stderr, "\x1b[0m");
+#endif
+ }
+}
+
+/* Deallocate a tree */
+static void destroy_func_tree(CheckasmFunc *const f) {
+ if (f) {
+ CheckasmFuncVersion *v = f->versions.next;
+ while (v) {
+ CheckasmFuncVersion *next = v->next;
+ free(v);
+ v = next;
+ }
+
+ destroy_func_tree(f->child[0]);
+ destroy_func_tree(f->child[1]);
+ free(f);
+ }
+}
+
+/* Allocate a zero-initialized block, clean up and exit on failure */
+static void *checkasm_malloc(const size_t size) {
+ void *const ptr = calloc(1, size);
+ if (!ptr) {
+ fprintf(stderr, "checkasm: malloc failed\n");
+ destroy_func_tree(state.funcs);
+ exit(1);
+ }
+ return ptr;
+}
+
+/* Get the suffix of the specified cpu flag */
+static const char *cpu_suffix(const unsigned cpu) {
+ for (int i = (int)(sizeof(cpus) / sizeof(*cpus)) - 2; i >= 0; i--)
+ if (cpu & cpus[i].flag)
+ return cpus[i].suffix;
+
+ return "c";
+}
+
+#ifdef readtime
+static int cmp_nop(const void *a, const void *b) {
+ return *(const uint16_t*)a - *(const uint16_t*)b;
+}
+
+/* Measure the overhead of the timing code (in decicycles) */
+static double measure_nop_time(void) {
+ uint16_t nops[10000];
+ int nop_sum = 0;
+
+ for (int i = 0; i < 10000; i++) {
+ uint64_t t = readtime();
+ nops[i] = (uint16_t) (readtime() - t);
+ }
+
+ qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
+ for (int i = 2500; i < 7500; i++)
+ nop_sum += nops[i];
+
+ return nop_sum / 5000.0;
+}
+
+static double avg_cycles_per_call(const CheckasmFuncVersion *const v) {
+ if (v->iterations) {
+ const double cycles = (double)v->cycles / v->iterations - state.nop_time;
+ if (cycles > 0.0)
+ return cycles / 4.0; /* 4 calls per iteration */
+ }
+ return 0.0;
+}
+
+/* Print benchmark results */
+static void print_benchs(const CheckasmFunc *const f) {
+ if (f) {
+ print_benchs(f->child[0]);
+
+ /* Only print functions with at least one assembly version */
+ const CheckasmFuncVersion *v = &f->versions;
+ if ((state.bench_c || v->cpu || v->next) && v->iterations) {
+ const double baseline = avg_cycles_per_call(v);
+ do {
+ const int pad_length = 10 + state.max_function_name_length -
+ printf("%s_%s:", f->name, cpu_suffix(v->cpu));
+ const double cycles = avg_cycles_per_call(v);
+ const double ratio = cycles ? baseline / cycles : 0.0;
+ printf("%*.1f (%5.2fx)\n", imax(pad_length, 0), cycles, ratio);
+ } while ((v = v->next));
+ }
+
+ print_benchs(f->child[1]);
+ }
+}
+#endif
+
+static void print_functions(const CheckasmFunc *const f) {
+ if (f) {
+ print_functions(f->child[0]);
+ const CheckasmFuncVersion *v = &f->versions;
+ printf("%s (%s", f->name, cpu_suffix(v->cpu));
+ while ((v = v->next))
+ printf(", %s", cpu_suffix(v->cpu));
+ printf(")\n");
+ print_functions(f->child[1]);
+ }
+}
+
+#define is_digit(x) ((x) >= '0' && (x) <= '9')
+
+/* ASCIIbetical sort except preserving natural order for numbers */
+static int cmp_func_names(const char *a, const char *b) {
+ const char *const start = a;
+ int ascii_diff, digit_diff;
+
+ for (; !(ascii_diff = *(const unsigned char*)a -
+ *(const unsigned char*)b) && *a; a++, b++);
+ for (; is_digit(*a) && is_digit(*b); a++, b++);
+
+ if (a > start && is_digit(a[-1]) &&
+ (digit_diff = is_digit(*a) - is_digit(*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 *const f, const int dir) {
+ CheckasmFunc *const 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 **const root) {
+ CheckasmFunc *const 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;
+ }
+ else 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(CheckasmFunc **const root, const char *const name) {
+ CheckasmFunc *f = *root;
+
+ if (f) {
+ /* Search the tree for a matching node */
+ const int cmp = cmp_func_names(name, f->name);
+ if (cmp) {
+ f = get_func(&f->child[cmp > 0], name);
+
+ /* 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 */
+ const size_t name_length = strlen(name) + 1;
+ f = *root = checkasm_malloc(offsetof(CheckasmFunc, name) + name_length);
+ memcpy(f->name, name, name_length);
+ }
+
+ return f;
+}
+
+checkasm_context checkasm_context_buf;
+
+/* Crash handling: attempt to catch crashes and handle them
+ * gracefully instead of just aborting abruptly. */
+#ifdef _WIN32
+static LONG NTAPI signal_handler(EXCEPTION_POINTERS *const e) {
+ if (!state.catch_signals)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ const char *err;
+ switch (e->ExceptionRecord->ExceptionCode) {
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ err = "fatal arithmetic error";
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ err = "illegal instruction";
+ break;
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_IN_PAGE_ERROR:
+ case EXCEPTION_STACK_OVERFLOW:
+ err = "segmentation fault";
+ break;
+ default:
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ state.catch_signals = 0;
+ checkasm_fail_func(err);
+ checkasm_load_context();
+ return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
+}
+#else
+static void signal_handler(const int s) {
+ if (state.catch_signals) {
+ state.catch_signals = 0;
+ checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
+ s == SIGILL ? "illegal instruction" :
+ "segmentation fault");
+ checkasm_load_context();
+ } else {
+ /* fall back to the default signal handler */
+ static const struct sigaction default_sa = { .sa_handler = SIG_DFL };
+ sigaction(s, &default_sa, NULL);
+ raise(s);
+ }
+}
+#endif
+
+/* Compares a string with a wildcard pattern. */
+static int wildstrcmp(const char *str, const char *pattern) {
+ const char *wild = strchr(pattern, '*');
+ if (wild) {
+ const size_t len = wild - pattern;
+ if (strncmp(str, pattern, len)) return 1;
+ while (*++wild == '*');
+ if (!*wild) return 0;
+ str += len;
+ while (*str && wildstrcmp(str, wild)) str++;
+ return !*str;
+ }
+ return strcmp(str, pattern);
+}
+
+/* Perform tests and benchmarks for the specified
+ * cpu flag if supported by the host */
+static void check_cpu_flag(const char *const name, unsigned flag) {
+ const unsigned old_cpu_flag = state.cpu_flag;
+
+ flag |= old_cpu_flag;
+ dav1d_set_cpu_flags_mask(flag);
+ state.cpu_flag = dav1d_get_cpu_flags();
+
+ if (!flag || state.cpu_flag != old_cpu_flag) {
+ state.cpu_flag_name = name;
+ state.suffix_length = (int)strlen(cpu_suffix(flag)) + 1;
+ for (int i = 0; tests[i].func; i++) {
+ if (state.test_pattern && wildstrcmp(tests[i].name, state.test_pattern))
+ continue;
+ xor128_srand(state.seed);
+ state.current_test_name = tests[i].name;
+ tests[i].func();
+ }
+ }
+}
+
+/* Print the name of the current CPU flag, but only do it once */
+static void print_cpu_name(void) {
+ if (state.cpu_flag_name) {
+ color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
+ state.cpu_flag_name = NULL;
+ }
+}
+
+static unsigned get_seed(void) {
+#ifdef _WIN32
+ LARGE_INTEGER i;
+ QueryPerformanceCounter(&i);
+ return i.LowPart;
+#elif defined(__APPLE__)
+ return (unsigned) mach_absolute_time();
+#else
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (unsigned) (1000000000ULL * ts.tv_sec + ts.tv_nsec);
+#endif
+}
+
+int main(int argc, char *argv[]) {
+ state.seed = get_seed();
+
+ while (argc > 1) {
+ if (!strncmp(argv[1], "--help", 6) || !strcmp(argv[1], "-h")) {
+ fprintf(stderr,
+ "checkasm [options] <random seed>\n"
+ " <random seed> Numeric value to seed the rng\n"
+ "Options:\n"
+ " --affinity=<cpu> Run the process on CPU <cpu>\n"
+ " --test=<pattern> Test only <pattern>\n"
+ " --function=<pattern> -f Test only the functions matching <pattern>\n"
+ " --bench -b Benchmark the tested functions\n"
+ " --list-functions List available functions\n"
+ " --list-tests List available tests\n"
+ " --bench-c -c Benchmark the C-only functions\n"
+ " --verbose -v Print verbose output\n");
+ return 0;
+ } else if (!strcmp(argv[1], "--bench-c") || !strcmp(argv[1], "-c")) {
+ state.bench_c = 1;
+ } else if (!strcmp(argv[1], "--bench") || !strcmp(argv[1], "-b")) {
+#ifndef readtime
+ fprintf(stderr,
+ "checkasm: --bench is not supported on your system\n");
+ return 1;
+#endif
+ state.bench = 1;
+ } else if (!strncmp(argv[1], "--test=", 7)) {
+ state.test_pattern = argv[1] + 7;
+ } else if (!strcmp(argv[1], "-t")) {
+ state.test_pattern = argc > 1 ? argv[2] : "";
+ argc--;
+ argv++;
+ } else if (!strncmp(argv[1], "--function=", 11)) {
+ state.function_pattern = argv[1] + 11;
+ } else if (!strcmp(argv[1], "-f")) {
+ state.function_pattern = argc > 1 ? argv[2] : "";
+ argc--;
+ argv++;
+ } else if (!strcmp(argv[1], "--list-functions")) {
+ state.function_listing = 1;
+ } else if (!strcmp(argv[1], "--list-tests")) {
+ for (int i = 0; tests[i].name; i++)
+ printf("%s\n", tests[i].name);
+ return 0;
+ } else if (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v")) {
+ state.verbose = 1;
+ } else if (!strncmp(argv[1], "--affinity=", 11)) {
+ unsigned long affinity = strtoul(argv[1] + 11, NULL, 16);
+#ifdef _WIN32
+ BOOL (WINAPI *spdcs)(HANDLE, const ULONG*, ULONG) =
+ (void*)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "SetProcessDefaultCpuSets");
+ HANDLE process = GetCurrentProcess();
+ int affinity_err;
+ if (spdcs) {
+ affinity_err = !spdcs(process, (ULONG[]){ affinity + 256 }, 1);
+ } else {
+ if (affinity < sizeof(DWORD_PTR) * 8)
+ affinity_err = !SetProcessAffinityMask(process, (DWORD_PTR)1 << affinity);
+ else
+ affinity_err = 1;
+ }
+ if (affinity_err) {
+ fprintf(stderr, "checkasm: invalid cpu affinity (%lu)\n", affinity);
+ return 1;
+ } else {
+ fprintf(stderr, "checkasm: running on cpu %lu\n", affinity);
+ }
+#elif defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(CPU_SET)
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(affinity, &set);
+ if (pthread_setaffinity_np(pthread_self(), sizeof(set), &set)) {
+ fprintf(stderr, "checkasm: invalid cpu affinity (%lu)\n", affinity);
+ return 1;
+ } else {
+ fprintf(stderr, "checkasm: running on cpu %lu\n", affinity);
+ }
+#else
+ (void)affinity;
+ fprintf(stderr,
+ "checkasm: --affinity is not supported on your system\n");
+ return 1;
+#endif
+ } else {
+ state.seed = (unsigned) strtoul(argv[1], NULL, 10);
+ }
+
+ argc--;
+ argv++;
+ }
+
+#if TRIM_DSP_FUNCTIONS
+ fprintf(stderr, "checkasm: reference functions unavailable\n");
+ return 0;
+#endif
+
+ dav1d_init_cpu();
+
+#ifdef _WIN32
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ AddVectoredExceptionHandler(0, signal_handler);
+#endif
+#else
+ const struct sigaction sa = {
+ .sa_handler = signal_handler,
+ .sa_flags = SA_NODEFER,
+ };
+ sigaction(SIGBUS, &sa, NULL);
+ sigaction(SIGFPE, &sa, NULL);
+ sigaction(SIGILL, &sa, NULL);
+ sigaction(SIGSEGV, &sa, NULL);
+#endif
+
+#ifdef readtime
+ if (state.bench) {
+ static int testing = 0;
+ checkasm_save_context();
+ if (!testing) {
+ checkasm_set_signal_handler_state(1);
+ testing = 1;
+ readtime();
+ checkasm_set_signal_handler_state(0);
+ } else {
+ fprintf(stderr, "checkasm: unable to access cycle counter\n");
+ return 1;
+ }
+ }
+#endif
+
+ int ret = 0;
+
+ if (!state.function_listing) {
+#if ARCH_X86_64
+ void checkasm_warmup_avx2(void);
+ void checkasm_warmup_avx512(void);
+ const unsigned cpu_flags = dav1d_get_cpu_flags();
+ if (cpu_flags & DAV1D_X86_CPU_FLAG_AVX512ICL)
+ state.simd_warmup = checkasm_warmup_avx512;
+ else if (cpu_flags & DAV1D_X86_CPU_FLAG_AVX2)
+ state.simd_warmup = checkasm_warmup_avx2;
+ checkasm_simd_warmup();
+#endif
+#if ARCH_X86
+ unsigned checkasm_init_x86(char *name);
+ char name[48];
+ const unsigned cpuid = checkasm_init_x86(name);
+ for (size_t len = strlen(name); len && name[len-1] == ' '; len--)
+ name[len-1] = '\0'; /* trim trailing whitespace */
+ fprintf(stderr, "checkasm: %s (%08X) using random seed %u\n", name, cpuid, state.seed);
+#else
+ fprintf(stderr, "checkasm: using random seed %u\n", state.seed);
+#endif
+ }
+
+ check_cpu_flag(NULL, 0);
+ for (int i = 0; cpus[i].flag; i++)
+ check_cpu_flag(cpus[i].name, cpus[i].flag);
+
+ if (state.function_listing) {
+ print_functions(state.funcs);
+ } else if (state.num_failed) {
+ fprintf(stderr, "checkasm: %d of %d tests failed\n",
+ state.num_failed, state.num_checked);
+ ret = 1;
+ } else {
+ if (state.num_checked)
+ fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
+ else
+ fprintf(stderr, "checkasm: no tests to perform\n");
+#ifdef readtime
+ if (state.bench && state.max_function_name_length) {
+ state.nop_time = measure_nop_time();
+ if (state.verbose)
+ printf("nop:%*.1f\n", state.max_function_name_length + 6, state.nop_time);
+ print_benchs(state.funcs);
+ }
+#endif
+ }
+
+ destroy_func_tree(state.funcs);
+ return ret;
+}
+
+/* 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 */
+void *checkasm_check_func(void *const func, const char *const name, ...) {
+ char name_buf[256];
+ va_list arg;
+
+ va_start(arg, name);
+ int name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
+ va_end(arg);
+
+ if (!func || name_length <= 0 || (size_t)name_length >= sizeof(name_buf) ||
+ (state.function_pattern && wildstrcmp(name_buf, state.function_pattern)))
+ {
+ return NULL;
+ }
+
+ state.current_func = get_func(&state.funcs, name_buf);
+
+ state.funcs->color = 1;
+ CheckasmFuncVersion *v = &state.current_func->versions;
+ void *ref = func;
+
+ if (v->func) {
+ CheckasmFuncVersion *prev;
+ do {
+ /* Only test functions that haven't already been tested */
+ if (v->func == func)
+ return NULL;
+
+ if (v->ok)
+ ref = v->func;
+
+ prev = v;
+ } while ((v = v->next));
+
+ v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
+ }
+
+ name_length += state.suffix_length;
+ if (name_length > state.max_function_name_length)
+ state.max_function_name_length = name_length;
+
+ v->func = func;
+ v->ok = 1;
+ v->cpu = state.cpu_flag;
+ state.current_func_ver = v;
+ if (state.function_listing) /* Save function names without running tests */
+ return NULL;
+
+ xor128_srand(state.seed);
+
+ if (state.cpu_flag)
+ state.num_checked++;
+
+ return ref;
+}
+
+/* Decide whether or not the current function needs to be benchmarked */
+int checkasm_bench_func(void) {
+ return !state.num_failed && state.bench;
+}
+
+/* Indicate that the current test has failed, return whether verbose printing
+ * is requested. */
+int checkasm_fail_func(const char *const msg, ...) {
+ if (state.current_func_ver && state.current_func_ver->cpu &&
+ state.current_func_ver->ok)
+ {
+ va_list arg;
+
+ print_cpu_name();
+ fprintf(stderr, " %s_%s (", state.current_func->name,
+ cpu_suffix(state.current_func_ver->cpu));
+ va_start(arg, msg);
+ vfprintf(stderr, msg, arg);
+ va_end(arg);
+ fprintf(stderr, ")\n");
+
+ state.current_func_ver->ok = 0;
+ state.num_failed++;
+ }
+ return state.verbose;
+}
+
+/* Update benchmark results of the current function */
+void checkasm_update_bench(const int iterations, const uint64_t cycles) {
+ state.current_func_ver->iterations += iterations;
+ state.current_func_ver->cycles += cycles;
+}
+
+/* Print the outcome of all tests performed since
+ * the last time this function was called */
+void checkasm_report(const char *const name, ...) {
+ static int prev_checked, prev_failed;
+ static size_t max_length;
+
+ if (state.num_checked > prev_checked) {
+ int pad_length = (int) max_length + 4;
+ va_list arg;
+
+ 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", imax(pad_length, 0) + 2, '[');
+
+ if (state.num_failed == prev_failed)
+ color_printf(COLOR_GREEN, "OK");
+ else
+ color_printf(COLOR_RED, "FAILED");
+ fprintf(stderr, "]\n");
+
+ prev_checked = state.num_checked;
+ prev_failed = state.num_failed;
+ } else if (!state.cpu_flag) {
+ /* Calculate the amount of padding required
+ * to make the output vertically aligned */
+ size_t 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;
+ }
+}
+
+void checkasm_set_signal_handler_state(const int enabled) {
+ state.catch_signals = enabled;
+}
+
+static int check_err(const char *const file, const int line,
+ const char *const name, const int w, const int h,
+ int *const err)
+{
+ if (*err)
+ return 0;
+ if (!checkasm_fail_func("%s:%d", file, line))
+ return 1;
+ *err = 1;
+ fprintf(stderr, "%s (%dx%d):\n", name, w, h);
+ return 0;
+}
+
+#define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
+int checkasm_check_##type(const char *const file, const int line, \
+ const type *buf1, ptrdiff_t stride1, \
+ const type *buf2, ptrdiff_t stride2, \
+ const int w, int h, const char *const name, \
+ const int align_w, const int align_h, \
+ const int padding) \
+{ \
+ int aligned_w = (w + align_w - 1) & ~(align_w - 1); \
+ int aligned_h = (h + align_h - 1) & ~(align_h - 1); \
+ int err = 0; \
+ stride1 /= sizeof(*buf1); \
+ stride2 /= sizeof(*buf2); \
+ int y = 0; \
+ for (y = 0; y < h; y++) \
+ if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \
+ break; \
+ if (y != h) { \
+ if (check_err(file, line, name, w, h, &err)) \
+ return 1; \
+ for (y = 0; y < h; y++) { \
+ for (int x = 0; x < w; x++) \
+ fprintf(stderr, " " fmt, buf1[x]); \
+ fprintf(stderr, " "); \
+ for (int x = 0; x < w; x++) \
+ fprintf(stderr, " " fmt, buf2[x]); \
+ fprintf(stderr, " "); \
+ for (int x = 0; x < w; x++) \
+ fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
+ buf1 += stride1; \
+ buf2 += stride2; \
+ fprintf(stderr, "\n"); \
+ } \
+ buf1 -= h*stride1; \
+ buf2 -= h*stride2; \
+ } \
+ for (y = -padding; y < 0; y++) \
+ if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
+ (w + 2*padding)*sizeof(*buf1))) { \
+ if (check_err(file, line, name, w, h, &err)) \
+ return 1; \
+ fprintf(stderr, " overwrite above\n"); \
+ break; \
+ } \
+ for (y = aligned_h; y < aligned_h + padding; y++) \
+ if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
+ (w + 2*padding)*sizeof(*buf1))) { \
+ if (check_err(file, line, name, w, h, &err)) \
+ return 1; \
+ fprintf(stderr, " overwrite below\n"); \
+ break; \
+ } \
+ for (y = 0; y < h; y++) \
+ if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
+ padding*sizeof(*buf1))) { \
+ if (check_err(file, line, name, w, h, &err)) \
+ return 1; \
+ fprintf(stderr, " overwrite left\n"); \
+ break; \
+ } \
+ for (y = 0; y < h; y++) \
+ if (memcmp(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \
+ padding*sizeof(*buf1))) { \
+ if (check_err(file, line, name, w, h, &err)) \
+ return 1; \
+ fprintf(stderr, " overwrite right\n"); \
+ break; \
+ } \
+ return err; \
+}
+
+DEF_CHECKASM_CHECK_FUNC(int8_t, "%4d")
+DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d")
+DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d")
+DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x")
+DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
+DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08x")
+
+#if ARCH_X86_64
+void checkasm_simd_warmup(void)
+{
+ if (state.simd_warmup)
+ state.simd_warmup();
+}
+#endif
diff --git a/third_party/dav1d/tests/checkasm/checkasm.h b/third_party/dav1d/tests/checkasm/checkasm.h
new file mode 100644
index 0000000000..29c1dbe2b9
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/checkasm.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_TESTS_CHECKASM_CHECKASM_H
+#define DAV1D_TESTS_CHECKASM_CHECKASM_H
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#if ARCH_X86_64 && defined(_WIN32)
+/* setjmp/longjmp on 64-bit Windows will try to use SEH to unwind the stack,
+ * which doesn't work for assembly functions without unwind information. */
+#include <windows.h>
+#define checkasm_context CONTEXT
+#define checkasm_save_context() RtlCaptureContext(&checkasm_context_buf)
+#define checkasm_load_context() RtlRestoreContext(&checkasm_context_buf, NULL)
+#else
+#include <setjmp.h>
+#define checkasm_context jmp_buf
+#define checkasm_save_context() setjmp(checkasm_context_buf)
+#define checkasm_load_context() longjmp(checkasm_context_buf, 1)
+#endif
+
+#include "include/common/attributes.h"
+#include "include/common/bitdepth.h"
+#include "include/common/intops.h"
+
+int xor128_rand(void);
+#define rnd xor128_rand
+
+#define decl_check_bitfns(name) \
+name##_8bpc(void); \
+name##_16bpc(void)
+
+void checkasm_check_msac(void);
+void checkasm_check_refmvs(void);
+decl_check_bitfns(void checkasm_check_cdef);
+decl_check_bitfns(void checkasm_check_filmgrain);
+decl_check_bitfns(void checkasm_check_ipred);
+decl_check_bitfns(void checkasm_check_itx);
+decl_check_bitfns(void checkasm_check_loopfilter);
+decl_check_bitfns(void checkasm_check_looprestoration);
+decl_check_bitfns(void checkasm_check_mc);
+
+void *checkasm_check_func(void *func, const char *name, ...);
+int checkasm_bench_func(void);
+int checkasm_fail_func(const char *msg, ...);
+void checkasm_update_bench(int iterations, uint64_t cycles);
+void checkasm_report(const char *name, ...);
+void checkasm_set_signal_handler_state(int enabled);
+extern checkasm_context checkasm_context_buf;
+
+/* float compare utilities */
+int float_near_ulp(float a, float b, unsigned max_ulp);
+int float_near_abs_eps(float a, float b, float eps);
+int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp);
+int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
+ int len);
+int float_near_abs_eps_array(const float *a, const float *b, float eps,
+ int len);
+int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
+ unsigned max_ulp, int len);
+
+#define BENCH_RUNS (1 << 12) /* Trade-off between accuracy and speed */
+
+/* Decide whether or not the specified function needs to be tested */
+#define check_func(func, ...)\
+ (func_ref = checkasm_check_func((func_new = func), __VA_ARGS__))
+
+/* Declare the function prototype. The first argument is the return value,
+ * the remaining arguments are the function parameters. Naming parameters
+ * is optional. */
+#define declare_func(ret, ...)\
+ declare_new(ret, __VA_ARGS__)\
+ void *func_ref, *func_new;\
+ typedef ret func_type(__VA_ARGS__);\
+ checkasm_save_context()
+
+/* Indicate that the current test has failed */
+#define fail() checkasm_fail_func("%s:%d", __FILE__, __LINE__)
+
+/* Print the test outcome */
+#define report checkasm_report
+
+/* Call the reference function */
+#define call_ref(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_ref)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
+
+#if HAVE_ASM
+#if ARCH_X86
+#if defined(_MSC_VER) && !defined(__clang__)
+#include <intrin.h>
+#define readtime() (_mm_lfence(), __rdtsc())
+#else
+static inline uint64_t readtime(void) {
+ uint32_t eax, edx;
+ __asm__ __volatile__("lfence\nrdtsc" : "=a"(eax), "=d"(edx));
+ return (((uint64_t)edx) << 32) | eax;
+}
+#define readtime readtime
+#endif
+#elif (ARCH_AARCH64 || ARCH_ARM) && defined(__APPLE__)
+#include <mach/mach_time.h>
+#define readtime() mach_absolute_time()
+#elif ARCH_AARCH64
+#ifdef _MSC_VER
+#include <windows.h>
+#define readtime() (_InstructionSynchronizationBarrier(), ReadTimeStampCounter())
+#else
+static inline uint64_t readtime(void) {
+ uint64_t cycle_counter;
+ /* This requires enabling user mode access to the cycle counter (which
+ * can only be done from kernel space).
+ * This could also read cntvct_el0 instead of pmccntr_el0; that register
+ * might also be readable (depending on kernel version), but it has much
+ * worse precision (it's a fixed 50 MHz timer). */
+ __asm__ __volatile__("isb\nmrs %0, pmccntr_el0"
+ : "=r"(cycle_counter)
+ :: "memory");
+ return cycle_counter;
+}
+#define readtime readtime
+#endif
+#elif ARCH_ARM && !defined(_MSC_VER) && __ARM_ARCH >= 7
+static inline uint64_t readtime(void) {
+ uint32_t cycle_counter;
+ /* This requires enabling user mode access to the cycle counter (which
+ * can only be done from kernel space). */
+ __asm__ __volatile__("isb\nmrc p15, 0, %0, c9, c13, 0"
+ : "=r"(cycle_counter)
+ :: "memory");
+ return cycle_counter;
+}
+#define readtime readtime
+#elif ARCH_PPC64LE
+static inline uint64_t readtime(void) {
+ uint32_t tbu, tbl, temp;
+
+ __asm__ __volatile__(
+ "1:\n"
+ "mfspr %2,269\n"
+ "mfspr %0,268\n"
+ "mfspr %1,269\n"
+ "cmpw %2,%1\n"
+ "bne 1b\n"
+ : "=r"(tbl), "=r"(tbu), "=r"(temp)
+ :
+ : "cc");
+
+ return (((uint64_t)tbu) << 32) | (uint64_t)tbl;
+}
+#define readtime readtime
+#endif
+
+/* Verifies that clobbered callee-saved registers
+ * are properly saved and restored */
+void checkasm_checked_call(void *func, ...);
+
+#if ARCH_X86_64
+/* YMM and ZMM registers on x86 are turned off to save power when they haven't
+ * been used for some period of time. When they are used there will be a
+ * "warmup" period during which performance will be reduced and inconsistent
+ * which is problematic when trying to benchmark individual functions. We can
+ * work around this by periodically issuing "dummy" instructions that uses
+ * those registers to keep them powered on. */
+void checkasm_simd_warmup(void);
+
+/* The upper 32 bits of 32-bit data types are undefined when passed as function
+ * parameters. In practice those bits usually end up being zero which may hide
+ * certain bugs, such as using a register containing undefined bits as a pointer
+ * offset, so we want to intentionally clobber those bits with junk to expose
+ * any issues. The following set of macros automatically calculates a bitmask
+ * specifying which parameters should have their upper halves clobbered. */
+#ifdef _WIN32
+/* Integer and floating-point parameters share "register slots". */
+#define IGNORED_FP_ARGS 0
+#else
+/* Up to 8 floating-point parameters are passed in XMM registers, which are
+ * handled orthogonally from integer parameters passed in GPR registers. */
+#define IGNORED_FP_ARGS 8
+#endif
+#ifdef HAVE_C11_GENERIC
+#define clobber_type(arg) _Generic((void (*)(void*, arg))NULL,\
+ void (*)(void*, int32_t ): clobber_mask |= 1 << mpos++,\
+ void (*)(void*, uint32_t): clobber_mask |= 1 << mpos++,\
+ void (*)(void*, float ): mpos += (fp_args++ >= IGNORED_FP_ARGS),\
+ void (*)(void*, double ): mpos += (fp_args++ >= IGNORED_FP_ARGS),\
+ default: mpos++)
+#define init_clobber_mask(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, ...)\
+ unsigned clobber_mask = 0;\
+ {\
+ int mpos = 0, fp_args = 0;\
+ clobber_type(a); clobber_type(b); clobber_type(c); clobber_type(d);\
+ clobber_type(e); clobber_type(f); clobber_type(g); clobber_type(h);\
+ clobber_type(i); clobber_type(j); clobber_type(k); clobber_type(l);\
+ clobber_type(m); clobber_type(n); clobber_type(o); clobber_type(p);\
+ }
+#else
+/* Skip parameter clobbering on compilers without support for _Generic() */
+#define init_clobber_mask(...) unsigned clobber_mask = 0
+#endif
+#define declare_new(ret, ...)\
+ ret (*checked_call)(__VA_ARGS__, int, int, int, int, int, int, int,\
+ int, int, int, int, int, int, int, int, int,\
+ void*, unsigned) =\
+ (void*)checkasm_checked_call;\
+ init_clobber_mask(__VA_ARGS__, void*, void*, void*, void*,\
+ void*, void*, void*, void*, void*, void*,\
+ void*, void*, void*, void*, void*);
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checkasm_simd_warmup(),\
+ checked_call(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8,\
+ 7, 6, 5, 4, 3, 2, 1, func_new, clobber_mask));\
+ checkasm_set_signal_handler_state(0)
+#elif ARCH_X86_32
+#define declare_new(ret, ...)\
+ ret (*checked_call)(void *, __VA_ARGS__, int, int, int, int, int, int,\
+ int, int, int, int, int, int, int, int, int) =\
+ (void *)checkasm_checked_call;
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checked_call(func_new, __VA_ARGS__, 15, 14, 13, 12,\
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1));\
+ checkasm_set_signal_handler_state(0)
+#elif ARCH_ARM
+/* Use a dummy argument, to offset the real parameters by 2, not only 1.
+ * This makes sure that potential 8-byte-alignment of parameters is kept
+ * the same even when the extra parameters have been removed. */
+void checkasm_checked_call_vfp(void *func, int dummy, ...);
+#define declare_new(ret, ...)\
+ ret (*checked_call)(void *, int dummy, __VA_ARGS__,\
+ int, int, int, int, int, int, int, int,\
+ int, int, int, int, int, int, int) =\
+ (void *)checkasm_checked_call_vfp;
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checked_call(func_new, 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0));\
+ checkasm_set_signal_handler_state(0)
+#elif ARCH_AARCH64 && !defined(__APPLE__)
+void checkasm_stack_clobber(uint64_t clobber, ...);
+#define declare_new(ret, ...)\
+ ret (*checked_call)(void *, int, int, int, int, int, int, int,\
+ __VA_ARGS__, int, int, int, int, int, int, int, int,\
+ int, int, int, int, int, int, int) =\
+ (void *)checkasm_checked_call;
+#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
+ CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
+ CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
+ CLOB, CLOB, CLOB, CLOB, CLOB),\
+ checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__,\
+ 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0));\
+ checkasm_set_signal_handler_state(0)
+#else
+#define declare_new(ret, ...)
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_new)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
+#endif
+#else /* HAVE_ASM */
+#define declare_new(ret, ...)
+/* Call the function */
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_new)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
+#endif /* HAVE_ASM */
+
+/* Benchmark the function */
+#ifdef readtime
+#define bench_new(...)\
+ do {\
+ if (checkasm_bench_func()) {\
+ func_type *const tfunc = func_new;\
+ checkasm_set_signal_handler_state(1);\
+ uint64_t tsum = 0;\
+ int tcount = 0;\
+ for (int ti = 0; ti < BENCH_RUNS; ti++) {\
+ uint64_t t = readtime();\
+ int talt = 0; (void)talt;\
+ tfunc(__VA_ARGS__);\
+ talt = 1;\
+ tfunc(__VA_ARGS__);\
+ talt = 0;\
+ tfunc(__VA_ARGS__);\
+ talt = 1;\
+ tfunc(__VA_ARGS__);\
+ t = readtime() - t;\
+ if (t*tcount <= tsum*4 && ti > 0) {\
+ tsum += t;\
+ tcount++;\
+ }\
+ }\
+ checkasm_set_signal_handler_state(0);\
+ checkasm_update_bench(tcount, tsum);\
+ } else {\
+ const int talt = 0; (void)talt;\
+ call_new(__VA_ARGS__);\
+ }\
+ } while (0)
+#else
+#define bench_new(...) do {} while (0)
+#endif
+
+/* Alternates between two pointers. Intended to be used within bench_new()
+ * calls for functions which modifies their input buffer(s) to ensure that
+ * throughput, and not latency, is measured. */
+#define alternate(a, b) (talt ? (b) : (a))
+
+#define ROUND_UP(x,a) (((x)+((a)-1)) & ~((a)-1))
+#define PIXEL_RECT(name, w, h) \
+ ALIGN_STK_64(pixel, name##_buf, ((h)+32)*(ROUND_UP(w,64)+64) + 64,); \
+ ptrdiff_t name##_stride = sizeof(pixel)*(ROUND_UP(w,64)+64); \
+ (void)name##_stride; \
+ pixel *name = name##_buf + (ROUND_UP(w,64)+64)*16 + 64
+
+#define CLEAR_PIXEL_RECT(name) \
+ memset(name##_buf, 0x99, sizeof(name##_buf)) \
+
+#define DECL_CHECKASM_CHECK_FUNC(type) \
+int checkasm_check_##type(const char *const file, const int line, \
+ const type *const buf1, const ptrdiff_t stride1, \
+ const type *const buf2, const ptrdiff_t stride2, \
+ const int w, const int h, const char *const name, \
+ const int align_w, const int align_h, \
+ const int padding)
+
+DECL_CHECKASM_CHECK_FUNC(int8_t);
+DECL_CHECKASM_CHECK_FUNC(int16_t);
+DECL_CHECKASM_CHECK_FUNC(int32_t);
+DECL_CHECKASM_CHECK_FUNC(uint8_t);
+DECL_CHECKASM_CHECK_FUNC(uint16_t);
+DECL_CHECKASM_CHECK_FUNC(uint32_t);
+
+#define CONCAT(a,b) a ## b
+
+#define checkasm_check2(prefix, ...) CONCAT(checkasm_check_, prefix)(__FILE__, __LINE__, __VA_ARGS__)
+#define checkasm_check(prefix, ...) checkasm_check2(prefix, __VA_ARGS__, 0, 0, 0)
+
+#ifdef BITDEPTH
+#define checkasm_check_pixel(...) checkasm_check(PIXEL_TYPE, __VA_ARGS__)
+#define checkasm_check_pixel_padded(...) checkasm_check2(PIXEL_TYPE, __VA_ARGS__, 1, 1, 8)
+#define checkasm_check_pixel_padded_align(...) checkasm_check2(PIXEL_TYPE, __VA_ARGS__, 8)
+#define checkasm_check_coef(...) checkasm_check(COEF_TYPE, __VA_ARGS__)
+#endif
+
+#endif /* DAV1D_TESTS_CHECKASM_CHECKASM_H */
diff --git a/third_party/dav1d/tests/checkasm/filmgrain.c b/third_party/dav1d/tests/checkasm/filmgrain.c
new file mode 100644
index 0000000000..638e83fd11
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/filmgrain.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright © 2019, VideoLAN and dav1d authors
+ * Copyright © 2019, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include <string.h>
+
+#include "src/levels.h"
+#include "src/filmgrain.h"
+#define UNIT_TEST 1
+#include "src/fg_apply_tmpl.c"
+
+#if BITDEPTH == 8
+#define checkasm_check_entry(...) checkasm_check(int8_t, __VA_ARGS__)
+#else
+#define checkasm_check_entry(...) checkasm_check(int16_t, __VA_ARGS__)
+#endif
+
+static const char ss_name[][4] = {
+ [DAV1D_PIXEL_LAYOUT_I420 - 1] = "420",
+ [DAV1D_PIXEL_LAYOUT_I422 - 1] = "422",
+ [DAV1D_PIXEL_LAYOUT_I444 - 1] = "444",
+};
+
+static void check_gen_grny(const Dav1dFilmGrainDSPContext *const dsp) {
+ ALIGN_STK_16(entry, grain_lut_c, GRAIN_HEIGHT,[GRAIN_WIDTH]);
+ ALIGN_STK_16(entry, grain_lut_a, GRAIN_HEIGHT + 1,[GRAIN_WIDTH]);
+
+ declare_func(void, entry grain_lut[][GRAIN_WIDTH],
+ const Dav1dFilmGrainData *data HIGHBD_DECL_SUFFIX);
+
+ for (int i = 0; i < 4; i++) {
+ if (check_func(dsp->generate_grain_y, "gen_grain_y_ar%d_%dbpc", i, BITDEPTH)) {
+ ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
+ fg_data[0].seed = rnd() & 0xFFFF;
+
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#endif
+
+ fg_data[0].grain_scale_shift = rnd() & 3;
+ fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
+ fg_data[0].ar_coeff_lag = i;
+ const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
+ for (int n = 0; n < num_y_pos; n++)
+ fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
+
+ call_ref(grain_lut_c, fg_data HIGHBD_TAIL_SUFFIX);
+ call_new(grain_lut_a, fg_data HIGHBD_TAIL_SUFFIX);
+ checkasm_check_entry(grain_lut_c[0], sizeof(entry) * GRAIN_WIDTH,
+ grain_lut_a[0], sizeof(entry) * GRAIN_WIDTH,
+ GRAIN_WIDTH, GRAIN_HEIGHT, "grain_lut");
+
+ bench_new(grain_lut_a, fg_data HIGHBD_TAIL_SUFFIX);
+ }
+ }
+
+ report("gen_grain_y");
+}
+
+static void check_gen_grnuv(const Dav1dFilmGrainDSPContext *const dsp) {
+ ALIGN_STK_16(entry, grain_lut_y, GRAIN_HEIGHT + 1,[GRAIN_WIDTH]);
+ ALIGN_STK_16(entry, grain_lut_c, GRAIN_HEIGHT, [GRAIN_WIDTH]);
+ ALIGN_STK_16(entry, grain_lut_a, GRAIN_HEIGHT + 1,[GRAIN_WIDTH]);
+
+ declare_func(void, entry grain_lut[][GRAIN_WIDTH],
+ const entry grain_lut_y[][GRAIN_WIDTH],
+ const Dav1dFilmGrainData *data, intptr_t uv HIGHBD_DECL_SUFFIX);
+
+ for (int layout_idx = 0; layout_idx < 3; layout_idx++) {
+ const enum Dav1dPixelLayout layout = layout_idx + 1;
+ const int ss_x = layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int ss_y = layout == DAV1D_PIXEL_LAYOUT_I420;
+
+ for (int i = 0; i < 4; i++) {
+ if (check_func(dsp->generate_grain_uv[layout_idx],
+ "gen_grain_uv_ar%d_%dbpc_%s",
+ i, BITDEPTH, ss_name[layout_idx]))
+ {
+ ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
+ fg_data[0].seed = rnd() & 0xFFFF;
+
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#endif
+
+ fg_data[0].num_y_points = rnd() & 1;
+ fg_data[0].grain_scale_shift = rnd() & 3;
+ fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
+ fg_data[0].ar_coeff_lag = i;
+ const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
+ for (int n = 0; n < num_y_pos; n++)
+ fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
+ dsp->generate_grain_y(grain_lut_y, fg_data HIGHBD_TAIL_SUFFIX);
+
+ const int uv = rnd() & 1;
+ const int num_uv_pos = num_y_pos + !!fg_data[0].num_y_points;
+ for (int n = 0; n < num_uv_pos; n++)
+ fg_data[0].ar_coeffs_uv[uv][n] = (rnd() & 0xff) - 128;
+ if (!fg_data[0].num_y_points)
+ fg_data[0].ar_coeffs_uv[uv][num_uv_pos] = 0;
+ memset(grain_lut_c, 0xff, sizeof(grain_lut_c));
+ memset(grain_lut_a, 0xff, sizeof(grain_lut_a));
+ call_ref(grain_lut_c, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
+ call_new(grain_lut_a, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
+ int w = ss_x ? 44 : GRAIN_WIDTH;
+ int h = ss_y ? 38 : GRAIN_HEIGHT;
+ checkasm_check_entry(grain_lut_c[0], sizeof(entry) * GRAIN_WIDTH,
+ grain_lut_a[0], sizeof(entry) * GRAIN_WIDTH,
+ w, h, "grain_lut");
+
+ bench_new(grain_lut_a, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+
+ report("gen_grain_uv");
+}
+
+static void check_fgy_sbrow(const Dav1dFilmGrainDSPContext *const dsp) {
+ PIXEL_RECT(c_dst, 128, 32);
+ PIXEL_RECT(a_dst, 128, 32);
+ PIXEL_RECT(src, 128, 32);
+ const ptrdiff_t stride = c_dst_stride;
+
+ declare_func(void, pixel *dst_row, const pixel *src_row, ptrdiff_t stride,
+ const Dav1dFilmGrainData *data, size_t pw,
+ const uint8_t scaling[SCALING_SIZE],
+ const entry grain_lut[][GRAIN_WIDTH],
+ int bh, int row_num HIGHBD_DECL_SUFFIX);
+
+ if (check_func(dsp->fgy_32x32xn, "fgy_32x32xn_%dbpc", BITDEPTH)) {
+ ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 16,);
+ ALIGN_STK_16(entry, grain_lut, GRAIN_HEIGHT + 1,[GRAIN_WIDTH]);
+ ALIGN_STK_64(uint8_t, scaling, SCALING_SIZE,);
+ fg_data[0].seed = rnd() & 0xFFFF;
+
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ fg_data[0].grain_scale_shift = rnd() & 3;
+ fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
+ fg_data[0].ar_coeff_lag = rnd() & 3;
+ const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
+ for (int n = 0; n < num_y_pos; n++)
+ fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
+ dsp->generate_grain_y(grain_lut, fg_data HIGHBD_TAIL_SUFFIX);
+
+ fg_data[0].num_y_points = 2 + (rnd() % 13);
+ const int pad = 0xff / fg_data[0].num_y_points;
+ for (int n = 0; n < fg_data[0].num_y_points; n++) {
+ fg_data[0].y_points[n][0] = 0xff * n / fg_data[0].num_y_points;
+ fg_data[0].y_points[n][0] += rnd() % pad;
+ fg_data[0].y_points[n][1] = rnd() & 0xff;
+ }
+ generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].y_points,
+ fg_data[0].num_y_points, scaling);
+
+ fg_data[0].clip_to_restricted_range = rnd() & 1;
+ fg_data[0].scaling_shift = (rnd() & 3) + 8;
+ for (fg_data[0].overlap_flag = 0; fg_data[0].overlap_flag <= 1;
+ fg_data[0].overlap_flag++)
+ {
+ for (int i = 0; i <= 2 * fg_data[0].overlap_flag; i++) {
+ int w, h, row_num;
+ if (fg_data[0].overlap_flag) {
+ w = 35 + (rnd() % 93);
+ if (i == 0) {
+ row_num = 0;
+ h = 1 + (rnd() % 31);
+ } else {
+ row_num = 1 + (rnd() & 0x7ff);
+ if (i == 1) {
+ h = 3 + (rnd() % 30);
+ } else {
+ h = 1 + (rnd() & 1);
+ }
+ }
+ } else {
+ w = 1 + (rnd() & 127);
+ h = 1 + (rnd() & 31);
+ row_num = rnd() & 0x7ff;
+ }
+
+ for (int y = 0; y < 32; y++) {
+ // Src pixels past the right edge can be uninitialized
+ for (int x = 0; x < 128; x++)
+ src[y * PXSTRIDE(stride) + x] = rnd();
+ for (int x = 0; x < w; x++)
+ src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
+ }
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+ call_ref(c_dst, src, stride, fg_data, w, scaling, grain_lut, h,
+ row_num HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, src, stride, fg_data, w, scaling, grain_lut, h,
+ row_num HIGHBD_TAIL_SUFFIX);
+
+ checkasm_check_pixel_padded_align(c_dst, stride, a_dst, stride,
+ w, h, "dst", 32, 2);
+ }
+ }
+ fg_data[0].overlap_flag = 1;
+ for (int y = 0; y < 32; y++) {
+ // Make sure all pixels are in range
+ for (int x = 0; x < 128; x++)
+ src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
+ }
+ bench_new(a_dst, src, stride, fg_data, 64, scaling, grain_lut, 32,
+ 1 HIGHBD_TAIL_SUFFIX);
+ }
+
+ report("fgy_32x32xn");
+}
+
+static void check_fguv_sbrow(const Dav1dFilmGrainDSPContext *const dsp) {
+ PIXEL_RECT(c_dst, 128, 32);
+ PIXEL_RECT(a_dst, 128, 32);
+ PIXEL_RECT(src, 128, 32);
+ PIXEL_RECT(luma_src, 128, 32);
+ const ptrdiff_t lstride = luma_src_stride;
+
+ declare_func(void, pixel *dst_row, const pixel *src_row, ptrdiff_t stride,
+ const Dav1dFilmGrainData *data, size_t pw,
+ const uint8_t scaling[SCALING_SIZE],
+ const entry grain_lut[][GRAIN_WIDTH], int bh, int row_num,
+ const pixel *luma_row, ptrdiff_t luma_stride, int uv_pl,
+ int is_identity HIGHBD_DECL_SUFFIX);
+
+ for (int layout_idx = 0; layout_idx < 3; layout_idx++) {
+ const enum Dav1dPixelLayout layout = layout_idx + 1;
+ const int ss_x = layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int ss_y = layout == DAV1D_PIXEL_LAYOUT_I420;
+ const ptrdiff_t stride = c_dst_stride;
+
+ for (int csfl = 0; csfl <= 1; csfl++) {
+ if (check_func(dsp->fguv_32x32xn[layout_idx],
+ "fguv_32x32xn_%dbpc_%s_csfl%d",
+ BITDEPTH, ss_name[layout_idx], csfl))
+ {
+ ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
+ ALIGN_STK_16(entry, grain_lut, 2,[GRAIN_HEIGHT + 1][GRAIN_WIDTH]);
+ ALIGN_STK_64(uint8_t, scaling, SCALING_SIZE,);
+
+ fg_data[0].seed = rnd() & 0xFFFF;
+
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ const int uv_pl = rnd() & 1;
+ const int is_identity = rnd() & 1;
+
+ fg_data[0].grain_scale_shift = rnd() & 3;
+ fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
+ fg_data[0].ar_coeff_lag = rnd() & 3;
+ fg_data[0].num_y_points = csfl ? 2 + (rnd() % 13) : 0;
+ const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
+ for (int n = 0; n < num_y_pos; n++)
+ fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
+ const int num_uv_pos = num_y_pos + 1;
+ for (int n = 0; n < num_uv_pos; n++)
+ fg_data[0].ar_coeffs_uv[uv_pl][n] = (rnd() & 0xff) - 128;
+ dsp->generate_grain_y(grain_lut[0], fg_data HIGHBD_TAIL_SUFFIX);
+ dsp->generate_grain_uv[layout_idx](grain_lut[1], grain_lut[0],
+ fg_data, uv_pl HIGHBD_TAIL_SUFFIX);
+
+ if (csfl) {
+ const int pad = 0xff / fg_data[0].num_y_points;
+ for (int n = 0; n < fg_data[0].num_y_points; n++) {
+ fg_data[0].y_points[n][0] = 0xff * n / fg_data[0].num_y_points;
+ fg_data[0].y_points[n][0] += rnd() % pad;
+ fg_data[0].y_points[n][1] = rnd() & 0xff;
+ }
+ generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].y_points,
+ fg_data[0].num_y_points, scaling);
+ } else {
+ fg_data[0].num_uv_points[uv_pl] = 2 + (rnd() % 9);
+ const int pad = 0xff / fg_data[0].num_uv_points[uv_pl];
+ for (int n = 0; n < fg_data[0].num_uv_points[uv_pl]; n++) {
+ fg_data[0].uv_points[uv_pl][n][0] = 0xff * n / fg_data[0].num_uv_points[uv_pl];
+ fg_data[0].uv_points[uv_pl][n][0] += rnd() % pad;
+ fg_data[0].uv_points[uv_pl][n][1] = rnd() & 0xff;
+ }
+ generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].uv_points[uv_pl],
+ fg_data[0].num_uv_points[uv_pl], scaling);
+
+ fg_data[0].uv_mult[uv_pl] = (rnd() & 0xff) - 128;
+ fg_data[0].uv_luma_mult[uv_pl] = (rnd() & 0xff) - 128;
+ fg_data[0].uv_offset[uv_pl] = (rnd() & 0x1ff) - 256;
+ }
+
+ fg_data[0].clip_to_restricted_range = rnd() & 1;
+ fg_data[0].scaling_shift = (rnd() & 3) + 8;
+ fg_data[0].chroma_scaling_from_luma = csfl;
+ for (fg_data[0].overlap_flag = 0; fg_data[0].overlap_flag <= 1;
+ fg_data[0].overlap_flag++)
+ {
+ for (int i = 0; i <= 2 * fg_data[0].overlap_flag; i++) {
+ int w, h, row_num;
+ if (fg_data[0].overlap_flag) {
+ w = (36 >> ss_x) + (rnd() % (92 >> ss_x));
+ if (i == 0) {
+ row_num = 0;
+ h = 1 + (rnd() & (31 >> ss_y));
+ } else {
+ row_num = 1 + (rnd() & 0x7ff);
+ if (i == 1) {
+ h = (ss_y ? 2 : 3) + (rnd() % (ss_y ? 15 : 30));
+ } else {
+ h = ss_y ? 1 : 1 + (rnd() & 1);
+ }
+ }
+ } else {
+ w = 1 + (rnd() & (127 >> ss_x));
+ h = 1 + (rnd() & (31 >> ss_y));
+ row_num = rnd() & 0x7ff;
+ }
+
+ for (int y = 0; y < 32; y++) {
+ // Src pixels past the right edge can be uninitialized
+ for (int x = 0; x < 128; x++) {
+ src[y * PXSTRIDE(stride) + x] = rnd();
+ luma_src[y * PXSTRIDE(lstride) + x] = rnd();
+ }
+ for (int x = 0; x < w; x++)
+ src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
+ for (int x = 0; x < (w << ss_x); x++)
+ luma_src[y * PXSTRIDE(lstride) + x] &= bitdepth_max;
+ }
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+ call_ref(c_dst, src, stride, fg_data, w, scaling, grain_lut[1], h,
+ row_num, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, src, stride, fg_data, w, scaling, grain_lut[1], h,
+ row_num, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
+
+ checkasm_check_pixel_padded_align(c_dst, stride,
+ a_dst, stride,
+ w, h, "dst",
+ 32 >> ss_x, 4);
+ }
+ }
+
+ fg_data[0].overlap_flag = 1;
+ for (int y = 0; y < 32; y++) {
+ // Make sure all pixels are in range
+ for (int x = 0; x < 128; x++) {
+ src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
+ luma_src[y * PXSTRIDE(lstride) + x] &= bitdepth_max;
+ }
+ }
+ bench_new(a_dst, src, stride, fg_data, 64 >> ss_x, scaling, grain_lut[1], 32 >> ss_y,
+ 1, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+
+ report("fguv_32x32xn");
+}
+
+void bitfn(checkasm_check_filmgrain)(void) {
+ Dav1dFilmGrainDSPContext c;
+
+ bitfn(dav1d_film_grain_dsp_init)(&c);
+
+ check_gen_grny(&c);
+ check_gen_grnuv(&c);
+ check_fgy_sbrow(&c);
+ check_fguv_sbrow(&c);
+}
diff --git a/third_party/dav1d/tests/checkasm/ipred.c b/third_party/dav1d/tests/checkasm/ipred.c
new file mode 100644
index 0000000000..3676b809b3
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/ipred.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+#include "src/ipred.h"
+#include "src/levels.h"
+
+#include <stdio.h>
+
+static const char *const intra_pred_mode_names[N_IMPL_INTRA_PRED_MODES] = {
+ [DC_PRED] = "dc",
+ [DC_128_PRED] = "dc_128",
+ [TOP_DC_PRED] = "dc_top",
+ [LEFT_DC_PRED] = "dc_left",
+ [HOR_PRED] = "h",
+ [VERT_PRED] = "v",
+ [PAETH_PRED] = "paeth",
+ [SMOOTH_PRED] = "smooth",
+ [SMOOTH_V_PRED] = "smooth_v",
+ [SMOOTH_H_PRED] = "smooth_h",
+ [Z1_PRED] = "z1",
+ [Z2_PRED] = "z2",
+ [Z3_PRED] = "z3",
+ [FILTER_PRED] = "filter"
+};
+
+static const char *const cfl_ac_names[3] = { "420", "422", "444" };
+
+static const char *const cfl_pred_mode_names[DC_128_PRED + 1] = {
+ [DC_PRED] = "cfl",
+ [DC_128_PRED] = "cfl_128",
+ [TOP_DC_PRED] = "cfl_top",
+ [LEFT_DC_PRED] = "cfl_left",
+};
+
+static const uint8_t z_angles[27] = {
+ 3, 6, 9,
+ 14, 17, 20, 23, 26, 29, 32,
+ 36, 39, 42, 45, 48, 51, 54,
+ 58, 61, 64, 67, 70, 73, 76,
+ 81, 84, 87
+};
+
+static void check_intra_pred(Dav1dIntraPredDSPContext *const c) {
+ PIXEL_RECT(c_dst, 64, 64);
+ PIXEL_RECT(a_dst, 64, 64);
+ ALIGN_STK_64(pixel, topleft_buf, 257,);
+ pixel *const topleft = topleft_buf + 128;
+
+ declare_func(void, pixel *dst, ptrdiff_t stride, const pixel *topleft,
+ int width, int height, int angle, int max_width, int max_height
+ HIGHBD_DECL_SUFFIX);
+
+ for (int mode = 0; mode < N_IMPL_INTRA_PRED_MODES; mode++) {
+ int bpc_min = BITDEPTH, bpc_max = BITDEPTH;
+ if (mode == FILTER_PRED && BITDEPTH == 16) {
+ bpc_min = 10;
+ bpc_max = 12;
+ }
+ for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2)
+ for (int w = 4; w <= (mode == FILTER_PRED ? 32 : 64); w <<= 1)
+ if (check_func(c->intra_pred[mode], "intra_pred_%s_w%d_%dbpc",
+ intra_pred_mode_names[mode], w, bpc))
+ {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4,
+ (mode == FILTER_PRED ? 32 : 64)); h <<= 1)
+ {
+ const ptrdiff_t stride = c_dst_stride;
+ int nb_iters = (mode >= Z1_PRED && mode <= Z3_PRED) ? 5 : 1;
+
+ for (int iter = 0; iter < nb_iters; iter++) {
+ int a = 0, maxw = 0, maxh = 0;
+ if (mode >= Z1_PRED && mode <= Z3_PRED) { /* angle */
+ a = (90 * (mode - Z1_PRED) + z_angles[rnd() % 27]) |
+ (rnd() & 0x600);
+ if (mode == Z2_PRED) {
+ maxw = rnd(), maxh = rnd();
+ maxw = 1 + (maxw & (maxw & 4096 ? 4095 : w - 1));
+ maxh = 1 + (maxh & (maxh & 4096 ? 4095 : h - 1));
+ }
+ } else if (mode == FILTER_PRED) /* filter_idx */
+ a = (rnd() % 5) | (rnd() & ~511);
+
+ int bitdepth_max;
+ if (bpc == 16)
+ bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+ else
+ bitdepth_max = (1 << bpc) - 1;
+
+ for (int i = -h * 2; i <= w * 2; i++)
+ topleft[i] = rnd() & bitdepth_max;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+ call_ref(c_dst, stride, topleft, w, h, a, maxw, maxh
+ HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, stride, topleft, w, h, a, maxw, maxh
+ HIGHBD_TAIL_SUFFIX);
+ if (checkasm_check_pixel_padded(c_dst, stride,
+ a_dst, stride,
+ w, h, "dst"))
+ {
+ if (mode == Z1_PRED || mode == Z3_PRED)
+ fprintf(stderr, "angle = %d (0x%03x)\n",
+ a & 0x1ff, a & 0x600);
+ else if (mode == Z2_PRED)
+ fprintf(stderr, "angle = %d (0x%03x), "
+ "max_width = %d, max_height = %d\n",
+ a & 0x1ff, a & 0x600, maxw, maxh);
+ else if (mode == FILTER_PRED)
+ fprintf(stderr, "filter_idx = %d\n", a & 0x1ff);
+ break;
+ }
+
+ bench_new(a_dst, stride, topleft, w, h, a, 128, 128
+ HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+ }
+ report("intra_pred");
+}
+
+static void check_cfl_ac(Dav1dIntraPredDSPContext *const c) {
+ ALIGN_STK_64(int16_t, c_dst, 32 * 32,);
+ ALIGN_STK_64(int16_t, a_dst, 32 * 32,);
+ ALIGN_STK_64(pixel, luma, 32 * 32,);
+
+ declare_func(void, int16_t *ac, const pixel *y, ptrdiff_t stride,
+ int w_pad, int h_pad, int cw, int ch);
+
+ for (int layout = 1; layout <= DAV1D_PIXEL_LAYOUT_I444; layout++) {
+ const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420;
+ const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int h_step = 2 >> ss_hor, v_step = 2 >> ss_ver;
+ for (int w = 4; w <= (32 >> ss_hor); w <<= 1)
+ if (check_func(c->cfl_ac[layout - 1], "cfl_ac_%s_w%d_%dbpc",
+ cfl_ac_names[layout - 1], w, BITDEPTH))
+ {
+ for (int h = imax(w / 4, 4);
+ h <= imin(w * 4, (32 >> ss_ver)); h <<= 1)
+ {
+ const ptrdiff_t stride = 32 * sizeof(pixel);
+ for (int w_pad = imax((w >> 2) - h_step, 0);
+ w_pad >= 0; w_pad -= h_step)
+ {
+ for (int h_pad = imax((h >> 2) - v_step, 0);
+ h_pad >= 0; h_pad -= v_step)
+ {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ for (int y = 0; y < (h << ss_ver); y++)
+ for (int x = 0; x < (w << ss_hor); x++)
+ luma[y * 32 + x] = rnd() & bitdepth_max;
+
+ call_ref(c_dst, luma, stride, w_pad, h_pad, w, h);
+ call_new(a_dst, luma, stride, w_pad, h_pad, w, h);
+ checkasm_check(int16_t, c_dst, w * sizeof(*c_dst),
+ a_dst, w * sizeof(*a_dst),
+ w, h, "dst");
+ }
+ }
+
+ bench_new(a_dst, luma, stride, 0, 0, w, h);
+ }
+ }
+ }
+ report("cfl_ac");
+}
+
+static void check_cfl_pred(Dav1dIntraPredDSPContext *const c) {
+ PIXEL_RECT(c_dst, 32, 32);
+ PIXEL_RECT(a_dst, 32, 32);
+ ALIGN_STK_64(int16_t, ac, 32 * 32,);
+ ALIGN_STK_64(pixel, topleft_buf, 257,);
+ pixel *const topleft = topleft_buf + 128;
+
+ declare_func(void, pixel *dst, ptrdiff_t stride, const pixel *topleft,
+ int width, int height, const int16_t *ac, int alpha
+ HIGHBD_DECL_SUFFIX);
+
+ for (int mode = 0; mode <= DC_128_PRED; mode += 1 + 2 * !mode)
+ for (int w = 4; w <= 32; w <<= 1)
+ if (check_func(c->cfl_pred[mode], "cfl_pred_%s_w%d_%dbpc",
+ cfl_pred_mode_names[mode], w, BITDEPTH))
+ {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 32); h <<= 1)
+ {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ int alpha = ((rnd() & 15) + 1) * (1 - (rnd() & 2));
+
+ for (int i = -h * 2; i <= w * 2; i++)
+ topleft[i] = rnd() & bitdepth_max;
+
+ int luma_avg = w * h >> 1;
+ for (int i = 0; i < w * h; i++)
+ luma_avg += ac[i] = rnd() & (bitdepth_max << 3);
+ luma_avg /= w * h;
+ for (int i = 0; i < w * h; i++)
+ ac[i] -= luma_avg;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, topleft, w, h, ac, alpha
+ HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, topleft, w, h, ac, alpha
+ HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(a_dst, a_dst_stride, topleft, w, h, ac, alpha
+ HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("cfl_pred");
+}
+
+static void check_pal_pred(Dav1dIntraPredDSPContext *const c) {
+ PIXEL_RECT(c_dst, 64, 64);
+ PIXEL_RECT(a_dst, 64, 64);
+ ALIGN_STK_64(uint8_t, idx, 64 * 64,);
+ ALIGN_STK_16(uint16_t, pal, 8,);
+
+ declare_func(void, pixel *dst, ptrdiff_t stride, const uint16_t *pal,
+ const uint8_t *idx, int w, int h);
+
+ for (int w = 4; w <= 64; w <<= 1)
+ if (check_func(c->pal_pred, "pal_pred_w%d_%dbpc", w, BITDEPTH))
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 64); h <<= 1)
+ {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < 8; i++)
+ pal[i] = rnd() & bitdepth_max;
+
+ for (int i = 0; i < w * h; i++)
+ idx[i] = rnd() & 7;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, pal, idx, w, h);
+ call_new(a_dst, a_dst_stride, pal, idx, w, h);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride,
+ a_dst, a_dst_stride, w, h, "dst");
+
+ bench_new(a_dst, a_dst_stride, pal, idx, w, h);
+ }
+ report("pal_pred");
+}
+
+void bitfn(checkasm_check_ipred)(void) {
+ Dav1dIntraPredDSPContext c;
+ bitfn(dav1d_intra_pred_dsp_init)(&c);
+
+ check_intra_pred(&c);
+ check_cfl_ac(&c);
+ check_cfl_pred(&c);
+ check_pal_pred(&c);
+}
diff --git a/third_party/dav1d/tests/checkasm/itx.c b/third_party/dav1d/tests/checkasm/itx.c
new file mode 100644
index 0000000000..c7cc411ff5
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/itx.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include <math.h>
+
+#include "src/itx.h"
+#include "src/levels.h"
+#include "src/scan.h"
+#include "src/tables.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.707106781186547524401
+#endif
+
+enum Tx1D { DCT, ADST, FLIPADST, IDENTITY, WHT };
+
+static const uint8_t itx_1d_types[N_TX_TYPES_PLUS_LL][2] = {
+ [DCT_DCT] = { DCT, DCT },
+ [ADST_DCT] = { DCT, ADST },
+ [DCT_ADST] = { ADST, DCT },
+ [ADST_ADST] = { ADST, ADST },
+ [FLIPADST_DCT] = { DCT, FLIPADST },
+ [DCT_FLIPADST] = { FLIPADST, DCT },
+ [FLIPADST_FLIPADST] = { FLIPADST, FLIPADST },
+ [ADST_FLIPADST] = { FLIPADST, ADST },
+ [FLIPADST_ADST] = { ADST, FLIPADST },
+ [IDTX] = { IDENTITY, IDENTITY },
+ [V_DCT] = { IDENTITY, DCT },
+ [H_DCT] = { DCT, IDENTITY },
+ [V_ADST] = { IDENTITY, ADST },
+ [H_ADST] = { ADST, IDENTITY },
+ [V_FLIPADST] = { IDENTITY, FLIPADST },
+ [H_FLIPADST] = { FLIPADST, IDENTITY },
+ [WHT_WHT] = { WHT, WHT },
+};
+
+static const char *const itx_1d_names[5] = {
+ [DCT] = "dct",
+ [ADST] = "adst",
+ [FLIPADST] = "flipadst",
+ [IDENTITY] = "identity",
+ [WHT] = "wht"
+};
+
+static const double scaling_factors[9] = {
+ 4.0000, /* 4x4 */
+ 4.0000 * M_SQRT1_2, /* 4x8 8x4 */
+ 2.0000, /* 4x16 8x8 16x4 */
+ 2.0000 * M_SQRT1_2, /* 8x16 16x8 */
+ 1.0000, /* 8x32 16x16 32x8 */
+ 0.5000 * M_SQRT1_2, /* 16x32 32x16 */
+ 0.2500, /* 16x64 32x32 64x16 */
+ 0.1250 * M_SQRT1_2, /* 32x64 64x32 */
+ 0.0625, /* 64x64 */
+};
+
+/* FIXME: Ensure that those forward transforms are similar to the real AV1
+ * transforms. The FLIPADST currently uses the ADST forward transform for
+ * example which is obviously "incorrect", but we're just using it for now
+ * since it does produce coefficients in the correct range at least. */
+
+/* DCT-II */
+static void fdct_1d(double *const out, const double *const in, const int sz) {
+ for (int i = 0; i < sz; i++) {
+ out[i] = 0.0;
+ for (int j = 0; j < sz; j++)
+ out[i] += in[j] * cos(M_PI * (2 * j + 1) * i / (sz * 2.0));
+ }
+ out[0] *= M_SQRT1_2;
+}
+
+/* See "Towards jointly optimal spatial prediction and adaptive transform in
+ * video/image coding", by J. Han, A. Saxena, and K. Rose
+ * IEEE Proc. ICASSP, pp. 726-729, Mar. 2010.
+ * and "A Butterfly Structured Design of The Hybrid Transform Coding Scheme",
+ * by Jingning Han, Yaowu Xu, and Debargha Mukherjee
+ * http://research.google.com/pubs/archive/41418.pdf
+ */
+static void fadst_1d(double *const out, const double *const in, const int sz) {
+ for (int i = 0; i < sz; i++) {
+ out[i] = 0.0;
+ for (int j = 0; j < sz; j++)
+ out[i] += in[j] * sin(M_PI *
+ (sz == 4 ? ( j + 1) * (2 * i + 1) / (8.0 + 1.0) :
+ (2 * j + 1) * (2 * i + 1) / (sz * 4.0)));
+ }
+}
+
+static void fwht4_1d(double *const out, const double *const in)
+{
+ const double t0 = in[0] + in[1];
+ const double t3 = in[3] - in[2];
+ const double t4 = (t0 - t3) * 0.5;
+ const double t1 = t4 - in[1];
+ const double t2 = t4 - in[2];
+ out[0] = t0 - t2;
+ out[1] = t2;
+ out[2] = t3 + t1;
+ out[3] = t1;
+}
+
+static int copy_subcoefs(coef *coeff,
+ const enum RectTxfmSize tx, const enum TxfmType txtp,
+ const int sw, const int sh, const int subsh)
+{
+ /* copy the topleft coefficients such that the return value (being the
+ * coefficient scantable index for the eob token) guarantees that only
+ * the topleft $sub out of $sz (where $sz >= $sub) coefficients in both
+ * dimensions are non-zero. This leads to braching to specific optimized
+ * simd versions (e.g. dc-only) so that we get full asm coverage in this
+ * test */
+
+ const enum TxClass tx_class = dav1d_tx_type_class[txtp];
+ const uint16_t *const scan = dav1d_scans[tx];
+ const int sub_high = subsh > 0 ? subsh * 8 - 1 : 0;
+ const int sub_low = subsh > 1 ? sub_high - 8 : 0;
+ int n, eob;
+
+ for (n = 0, eob = 0; n < sw * sh; n++) {
+ int rc, rcx, rcy;
+ if (tx_class == TX_CLASS_2D)
+ rc = scan[n], rcx = rc % sh, rcy = rc / sh;
+ else if (tx_class == TX_CLASS_H)
+ rcx = n % sh, rcy = n / sh, rc = n;
+ else /* tx_class == TX_CLASS_V */
+ rcx = n / sw, rcy = n % sw, rc = rcy * sh + rcx;
+
+ /* Pick a random eob within this sub-itx */
+ if (rcx > sub_high || rcy > sub_high) {
+ break; /* upper boundary */
+ } else if (!eob && (rcx > sub_low || rcy > sub_low))
+ eob = n; /* lower boundary */
+ }
+
+ if (eob)
+ eob += rnd() % (n - eob - 1);
+ if (tx_class == TX_CLASS_2D)
+ for (n = eob + 1; n < sw * sh; n++)
+ coeff[scan[n]] = 0;
+ else if (tx_class == TX_CLASS_H)
+ for (n = eob + 1; n < sw * sh; n++)
+ coeff[n] = 0;
+ else /* tx_class == TX_CLASS_V */ {
+ for (int rcx = eob / sw, rcy = eob % sw; rcx < sh; rcx++, rcy = -1)
+ while (++rcy < sw)
+ coeff[rcy * sh + rcx] = 0;
+ n = sw * sh;
+ }
+ for (; n < 32 * 32; n++)
+ coeff[n] = rnd();
+ return eob;
+}
+
+static int ftx(coef *const buf, const enum RectTxfmSize tx,
+ const enum TxfmType txtp, const int w, const int h,
+ const int subsh, const int bitdepth_max)
+{
+ double out[64 * 64], temp[64 * 64];
+ const double scale = scaling_factors[ctz(w * h) - 4];
+ const int sw = imin(w, 32), sh = imin(h, 32);
+
+ for (int i = 0; i < h; i++) {
+ double in[64], temp_out[64];
+
+ for (int i = 0; i < w; i++)
+ in[i] = (rnd() & (2 * bitdepth_max + 1)) - bitdepth_max;
+
+ switch (itx_1d_types[txtp][0]) {
+ case DCT:
+ fdct_1d(temp_out, in, w);
+ break;
+ case ADST:
+ case FLIPADST:
+ fadst_1d(temp_out, in, w);
+ break;
+ case WHT:
+ fwht4_1d(temp_out, in);
+ break;
+ case IDENTITY:
+ memcpy(temp_out, in, w * sizeof(*temp_out));
+ break;
+ }
+
+ for (int j = 0; j < w; j++)
+ temp[j * h + i] = temp_out[j] * scale;
+ }
+
+ for (int i = 0; i < w; i++) {
+ switch (itx_1d_types[txtp][0]) {
+ case DCT:
+ fdct_1d(&out[i * h], &temp[i * h], h);
+ break;
+ case ADST:
+ case FLIPADST:
+ fadst_1d(&out[i * h], &temp[i * h], h);
+ break;
+ case WHT:
+ fwht4_1d(&out[i * h], &temp[i * h]);
+ break;
+ case IDENTITY:
+ memcpy(&out[i * h], &temp[i * h], h * sizeof(*out));
+ break;
+ }
+ }
+
+ for (int y = 0; y < sh; y++)
+ for (int x = 0; x < sw; x++)
+ buf[y * sw + x] = (coef) (out[y * w + x] + 0.5);
+
+ return copy_subcoefs(buf, tx, txtp, sw, sh, subsh);
+}
+
+static void check_itxfm_add(Dav1dInvTxfmDSPContext *const c,
+ const enum RectTxfmSize tx)
+{
+ ALIGN_STK_64(coef, coeff, 2, [32 * 32]);
+ PIXEL_RECT(c_dst, 64, 64);
+ PIXEL_RECT(a_dst, 64, 64);
+
+ static const uint8_t subsh_iters[5] = { 2, 2, 3, 5, 5 };
+
+ const int w = dav1d_txfm_dimensions[tx].w * 4;
+ const int h = dav1d_txfm_dimensions[tx].h * 4;
+ const int subsh_max = subsh_iters[imax(dav1d_txfm_dimensions[tx].lw,
+ dav1d_txfm_dimensions[tx].lh)];
+#if BITDEPTH == 16
+ const int bpc_min = 10, bpc_max = 12;
+#else
+ const int bpc_min = 8, bpc_max = 8;
+#endif
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, coef *coeff,
+ int eob HIGHBD_DECL_SUFFIX);
+
+ for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
+ bitfn(dav1d_itx_dsp_init)(c, bpc);
+ for (enum TxfmType txtp = 0; txtp < N_TX_TYPES_PLUS_LL; txtp++)
+ for (int subsh = 0; subsh < subsh_max; subsh++)
+ if (check_func(c->itxfm_add[tx][txtp],
+ "inv_txfm_add_%dx%d_%s_%s_%d_%dbpc",
+ w, h, itx_1d_names[itx_1d_types[txtp][0]],
+ itx_1d_names[itx_1d_types[txtp][1]], subsh,
+ bpc))
+ {
+ const int bitdepth_max = (1 << bpc) - 1;
+ const int eob = ftx(coeff[0], tx, txtp, w, h, subsh, bitdepth_max);
+ memcpy(coeff[1], coeff[0], sizeof(*coeff));
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ c_dst[y*PXSTRIDE(c_dst_stride) + x] =
+ a_dst[y*PXSTRIDE(a_dst_stride) + x] = rnd() & bitdepth_max;
+
+ call_ref(c_dst, c_dst_stride, coeff[0], eob
+ HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, coeff[1], eob
+ HIGHBD_TAIL_SUFFIX);
+
+ checkasm_check_pixel_padded(c_dst, c_dst_stride,
+ a_dst, a_dst_stride,
+ w, h, "dst");
+ if (memcmp(coeff[0], coeff[1], sizeof(*coeff)))
+ fail();
+
+ bench_new(alternate(c_dst, a_dst), a_dst_stride,
+ alternate(coeff[0], coeff[1]), eob HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("add_%dx%d", w, h);
+}
+
+void bitfn(checkasm_check_itx)(void) {
+ static const uint8_t txfm_size_order[N_RECT_TX_SIZES] = {
+ TX_4X4, RTX_4X8, RTX_4X16,
+ RTX_8X4, TX_8X8, RTX_8X16, RTX_8X32,
+ RTX_16X4, RTX_16X8, TX_16X16, RTX_16X32, RTX_16X64,
+ RTX_32X8, RTX_32X16, TX_32X32, RTX_32X64,
+ RTX_64X16, RTX_64X32, TX_64X64
+ };
+
+ /* Zero unused function pointer elements. */
+ Dav1dInvTxfmDSPContext c = { { { 0 } } };
+
+ for (int i = 0; i < N_RECT_TX_SIZES; i++)
+ check_itxfm_add(&c, txfm_size_order[i]);
+}
diff --git a/third_party/dav1d/tests/checkasm/loopfilter.c b/third_party/dav1d/tests/checkasm/loopfilter.c
new file mode 100644
index 0000000000..7d70d0648c
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/loopfilter.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include <string.h>
+
+#include "src/levels.h"
+#include "src/loopfilter.h"
+
+static void init_lpf_border(pixel *const dst, const ptrdiff_t stride,
+ int E, int I, const int bitdepth_max)
+{
+ const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8;
+ const int F = 1 << bitdepth_min_8;
+ E <<= bitdepth_min_8;
+ I <<= bitdepth_min_8;
+
+ const int filter_type = rnd() % 4;
+ const int edge_diff = rnd() % ((E + 2) * 4) - 2 * (E + 2);
+ switch (filter_type) {
+ case 0: // random, unfiltered
+ for (int i = -8; i < 8; i++)
+ dst[i * stride] = rnd() & bitdepth_max;
+ break;
+ case 1: // long flat
+ dst[-8 * stride] = rnd() & bitdepth_max;
+ dst[+7 * stride] = rnd() & bitdepth_max;
+ dst[+0 * stride] = rnd() & bitdepth_max;
+ dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
+ for (int i = 1; i < 7; i++) {
+ dst[-(1 + i) * stride] = iclip_pixel(dst[-1 * stride] +
+ rnd() % (2 * (F + 1)) - (F + 1));
+ dst[+(0 + i) * stride] = iclip_pixel(dst[+0 * stride] +
+ rnd() % (2 * (F + 1)) - (F + 1));
+ }
+ break;
+ case 2: // short flat
+ for (int i = 4; i < 8; i++) {
+ dst[-(1 + i) * stride] = rnd() & bitdepth_max;
+ dst[+(0 + i) * stride] = rnd() & bitdepth_max;
+ }
+ dst[+0 * stride] = rnd() & bitdepth_max;
+ dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
+ for (int i = 1; i < 4; i++) {
+ dst[-(1 + i) * stride] = iclip_pixel(dst[-1 * stride] +
+ rnd() % (2 * (F + 1)) - (F + 1));
+ dst[+(0 + i) * stride] = iclip_pixel(dst[+0 * stride] +
+ rnd() % (2 * (F + 1)) - (F + 1));
+ }
+ break;
+ case 3: // normal or hev
+ for (int i = 4; i < 8; i++) {
+ dst[-(1 + i) * stride] = rnd() & bitdepth_max;
+ dst[+(0 + i) * stride] = rnd() & bitdepth_max;
+ }
+ dst[+0 * stride] = rnd() & bitdepth_max;
+ dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
+ for (int i = 1; i < 4; i++) {
+ dst[-(1 + i) * stride] = iclip_pixel(dst[-(0 + i) * stride] +
+ rnd() % (2 * (I + 1)) - (I + 1));
+ dst[+(0 + i) * stride] = iclip_pixel(dst[+(i - 1) * stride] +
+ rnd() % (2 * (I + 1)) - (I + 1));
+ }
+ break;
+ }
+}
+
+static void check_lpf_sb(loopfilter_sb_fn fn, const char *const name,
+ const int n_blks, const int lf_idx,
+ const int is_chroma, const int dir)
+{
+ ALIGN_STK_64(pixel, c_dst_mem, 128 * 16,);
+ ALIGN_STK_64(pixel, a_dst_mem, 128 * 16,);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const uint32_t *mask,
+ const uint8_t (*l)[4], ptrdiff_t b4_stride,
+ const Av1FilterLUT *lut, int w HIGHBD_DECL_SUFFIX);
+
+ pixel *a_dst, *c_dst;
+ ptrdiff_t stride, b4_stride;
+ int w, h;
+ if (dir) {
+ a_dst = a_dst_mem + n_blks * 4 * 8;
+ c_dst = c_dst_mem + n_blks * 4 * 8;
+ w = n_blks * 4;
+ h = 16;
+ b4_stride = 32;
+ } else {
+ a_dst = a_dst_mem + 8;
+ c_dst = c_dst_mem + 8;
+ w = 16;
+ h = n_blks * 4;
+ b4_stride = 2;
+ }
+ stride = w * sizeof(pixel);
+
+ Av1FilterLUT lut;
+ const int sharp = rnd() & 7;
+ for (int level = 0; level < 64; level++) {
+ int limit = level;
+
+ if (sharp > 0) {
+ limit >>= (sharp + 3) >> 2;
+ limit = imin(limit, 9 - sharp);
+ }
+ limit = imax(limit, 1);
+
+ lut.i[level] = limit;
+ lut.e[level] = 2 * (level + 2) + limit;
+ }
+ lut.sharp[0] = (sharp + 3) >> 2;
+ lut.sharp[1] = sharp ? 9 - sharp : 0xff;
+
+ const int n_strengths = is_chroma ? 2 : 3;
+ for (int i = 0; i < n_strengths; i++) {
+ if (check_func(fn, "%s_w%d_%dbpc", name,
+ is_chroma ? 4 + 2 * i : 4 << i, BITDEPTH))
+ {
+ uint32_t vmask[4] = { 0 };
+ uint8_t l[32 * 2][4];
+
+ for (int j = 0; j < n_blks; j++) {
+ const int idx = rnd() % (i + 2);
+ if (idx) vmask[idx - 1] |= 1U << j;
+ if (dir) {
+ l[j][lf_idx] = rnd() & 63;
+ l[j + 32][lf_idx] = rnd() & 63;
+ } else {
+ l[j * 2][lf_idx] = rnd() & 63;
+ l[j * 2 + 1][lf_idx] = rnd() & 63;
+ }
+ }
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < 4 * n_blks; i++) {
+ const int x = i >> 2;
+ int L;
+ if (dir) {
+ L = l[32 + x][lf_idx] ? l[32 + x][lf_idx] : l[x][lf_idx];
+ } else {
+ L = l[2 * x + 1][lf_idx] ? l[2 * x + 1][lf_idx] : l[2 * x][lf_idx];
+ }
+ init_lpf_border(c_dst + i * (dir ? 1 : 16), dir ? n_blks * 4 : 1,
+ lut.e[L], lut.i[L], bitdepth_max);
+ }
+ memcpy(a_dst_mem, c_dst_mem, 128 * sizeof(pixel) * 16);
+
+ call_ref(c_dst, stride, vmask,
+ (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx],
+ b4_stride, &lut, n_blks HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, stride, vmask,
+ (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx],
+ b4_stride, &lut, n_blks HIGHBD_TAIL_SUFFIX);
+
+ checkasm_check_pixel(c_dst_mem, stride, a_dst_mem, stride,
+ w, h, "dst");
+ bench_new(alternate(c_dst, a_dst), stride, vmask,
+ (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx],
+ b4_stride, &lut, n_blks HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report(name);
+}
+
+void bitfn(checkasm_check_loopfilter)(void) {
+ Dav1dLoopFilterDSPContext c;
+
+ bitfn(dav1d_loop_filter_dsp_init)(&c);
+
+ check_lpf_sb(c.loop_filter_sb[0][0], "lpf_h_sb_y", 32, 0, 0, 0);
+ check_lpf_sb(c.loop_filter_sb[0][1], "lpf_v_sb_y", 32, 1, 0, 1);
+ check_lpf_sb(c.loop_filter_sb[1][0], "lpf_h_sb_uv", 16, 2, 1, 0);
+ check_lpf_sb(c.loop_filter_sb[1][1], "lpf_v_sb_uv", 16, 2, 1, 1);
+}
diff --git a/third_party/dav1d/tests/checkasm/looprestoration.c b/third_party/dav1d/tests/checkasm/looprestoration.c
new file mode 100644
index 0000000000..d84f3c476a
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/looprestoration.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/levels.h"
+#include "src/looprestoration.h"
+#include "src/tables.h"
+
+static int to_binary(int x) { /* 0-15 -> 0000-1111 */
+ return (x & 1) + 5 * (x & 2) + 25 * (x & 4) + 125 * (x & 8);
+}
+
+static void init_tmp(pixel *buf, const ptrdiff_t stride,
+ const int w, const int h, const int bitdepth_max)
+{
+ const int noise_mask = bitdepth_max >> 4;
+ const int x_off = rnd() & 7, y_off = rnd() & 7;
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ buf[x] = (((x + x_off) ^ (y + y_off)) & 8 ? bitdepth_max : 0) ^
+ (rnd() & noise_mask);
+ }
+ buf += PXSTRIDE(stride);
+ }
+}
+
+static void check_wiener(Dav1dLoopRestorationDSPContext *const c, const int bpc) {
+ ALIGN_STK_64(pixel, c_src, 448 * 64 + 64,), *const c_dst = c_src + 64;
+ ALIGN_STK_64(pixel, a_src, 448 * 64 + 64,), *const a_dst = a_src + 64;
+ ALIGN_STK_64(pixel, edge_buf, 448 * 8 + 64,), *const h_edge = edge_buf + 64;
+ pixel left[64][4];
+ LooprestorationParams params;
+ int16_t (*const filter)[8] = params.filter;
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride,
+ const pixel (*const left)[4],
+ const pixel *lpf, int w, int h,
+ const LooprestorationParams *params,
+ enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX);
+
+ for (int t = 0; t < 2; t++) {
+ if (check_func(c->wiener[t], "wiener_%dtap_%dbpc", t ? 5 : 7, bpc)) {
+ filter[0][0] = filter[0][6] = t ? 0 : (rnd() & 15) - 5;
+ filter[0][1] = filter[0][5] = (rnd() & 31) - 23;
+ filter[0][2] = filter[0][4] = (rnd() & 63) - 17;
+ filter[0][3] = -(filter[0][0] + filter[0][1] + filter[0][2]) * 2;
+#if BITDEPTH != 8
+ filter[0][3] += 128;
+#endif
+
+ filter[1][0] = filter[1][6] = t ? 0 : (rnd() & 15) - 5;
+ filter[1][1] = filter[1][5] = (rnd() & 31) - 23;
+ filter[1][2] = filter[1][4] = (rnd() & 63) - 17;
+ filter[1][3] = 128 - (filter[1][0] + filter[1][1] + filter[1][2]) * 2;
+
+ const int base_w = 1 + (rnd() % 384);
+ const int base_h = 1 + (rnd() & 63);
+ const int bitdepth_max = (1 << bpc) - 1;
+
+ init_tmp(c_src, 448 * sizeof(pixel), 448, 64, bitdepth_max);
+ init_tmp(edge_buf, 448 * sizeof(pixel), 448, 8, bitdepth_max);
+ init_tmp((pixel *) left, 4 * sizeof(pixel), 4, 64, bitdepth_max);
+
+ for (enum LrEdgeFlags edges = 0; edges <= 0xf; edges++) {
+ const int w = edges & LR_HAVE_RIGHT ? 256 : base_w;
+ const int h = edges & LR_HAVE_BOTTOM ? 64 : base_h;
+
+ memcpy(a_src, c_src, 448 * 64 * sizeof(pixel));
+
+ call_ref(c_dst, 448 * sizeof(pixel), left,
+ h_edge, w, h, &params, edges HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, 448 * sizeof(pixel), left,
+ h_edge, w, h, &params, edges HIGHBD_TAIL_SUFFIX);
+ if (checkasm_check_pixel(c_dst, 448 * sizeof(pixel),
+ a_dst, 448 * sizeof(pixel),
+ w, h, "dst"))
+ {
+ fprintf(stderr, "size = %dx%d, edges = %04d\n",
+ w, h, to_binary(edges));
+ break;
+ }
+ }
+ bench_new(alternate(c_dst, a_dst), 448 * sizeof(pixel), left,
+ h_edge, 256, 64, &params, 0xf HIGHBD_TAIL_SUFFIX);
+ }
+ }
+}
+
+static void check_sgr(Dav1dLoopRestorationDSPContext *const c, const int bpc) {
+ ALIGN_STK_64(pixel, c_src, 448 * 64 + 64,), *const c_dst = c_src + 64;
+ ALIGN_STK_64(pixel, a_src, 448 * 64 + 64,), *const a_dst = a_src + 64;
+ ALIGN_STK_64(pixel, edge_buf, 448 * 8 + 64,), *const h_edge = edge_buf + 64;
+ pixel left[64][4];
+ LooprestorationParams params;
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride,
+ const pixel (*const left)[4],
+ const pixel *lpf, int w, int h,
+ const LooprestorationParams *params,
+ enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX);
+
+ static const struct { char name[4]; uint8_t idx; } sgr_data[3] = {
+ { "5x5", 14 },
+ { "3x3", 10 },
+ { "mix", 0 },
+ };
+
+ for (int i = 0; i < 3; i++) {
+ if (check_func(c->sgr[i], "sgr_%s_%dbpc", sgr_data[i].name, bpc)) {
+ const uint16_t *const sgr_params = dav1d_sgr_params[sgr_data[i].idx];
+ params.sgr.s0 = sgr_params[0];
+ params.sgr.s1 = sgr_params[1];
+ params.sgr.w0 = sgr_params[0] ? (rnd() & 127) - 96 : 0;
+ params.sgr.w1 = (sgr_params[1] ? 160 - (rnd() & 127) : 33) - params.sgr.w0;
+
+ const int base_w = 1 + (rnd() % 384);
+ const int base_h = 1 + (rnd() & 63);
+ const int bitdepth_max = (1 << bpc) - 1;
+
+ init_tmp(c_src, 448 * sizeof(pixel), 448, 64, bitdepth_max);
+ init_tmp(edge_buf, 448 * sizeof(pixel), 448, 8, bitdepth_max);
+ init_tmp((pixel *) left, 4 * sizeof(pixel), 4, 64, bitdepth_max);
+
+ for (enum LrEdgeFlags edges = 0; edges <= 0xf; edges++) {
+ const int w = edges & LR_HAVE_RIGHT ? 256 : base_w;
+ const int h = edges & LR_HAVE_BOTTOM ? 64 : base_h;
+
+ memcpy(a_src, c_src, 448 * 64 * sizeof(pixel));
+
+ call_ref(c_dst, 448 * sizeof(pixel), left, h_edge,
+ w, h, &params, edges HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, 448 * sizeof(pixel), left, h_edge,
+ w, h, &params, edges HIGHBD_TAIL_SUFFIX);
+ if (checkasm_check_pixel(c_dst, 448 * sizeof(pixel),
+ a_dst, 448 * sizeof(pixel),
+ w, h, "dst"))
+ {
+ fprintf(stderr, "size = %dx%d, edges = %04d\n",
+ w, h, to_binary(edges));
+ break;
+ }
+ }
+ bench_new(alternate(c_dst, a_dst), 448 * sizeof(pixel), left,
+ h_edge, 256, 64, &params, 0xf HIGHBD_TAIL_SUFFIX);
+ }
+ }
+}
+
+void bitfn(checkasm_check_looprestoration)(void) {
+#if BITDEPTH == 16
+ const int bpc_min = 10, bpc_max = 12;
+#else
+ const int bpc_min = 8, bpc_max = 8;
+#endif
+ for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
+ Dav1dLoopRestorationDSPContext c;
+ bitfn(dav1d_loop_restoration_dsp_init)(&c, bpc);
+ check_wiener(&c, bpc);
+ }
+ report("wiener");
+ for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
+ Dav1dLoopRestorationDSPContext c;
+ bitfn(dav1d_loop_restoration_dsp_init)(&c, bpc);
+ check_sgr(&c, bpc);
+ }
+ report("sgr");
+}
diff --git a/third_party/dav1d/tests/checkasm/mc.c b/third_party/dav1d/tests/checkasm/mc.c
new file mode 100644
index 0000000000..047ef7b4a4
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/mc.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include "src/levels.h"
+#include "src/mc.h"
+
+static const char *const filter_names[] = {
+ "8tap_regular", "8tap_regular_smooth", "8tap_regular_sharp",
+ "8tap_sharp_regular", "8tap_sharp_smooth", "8tap_sharp",
+ "8tap_smooth_regular", "8tap_smooth", "8tap_smooth_sharp",
+ "bilinear"
+};
+
+static const char *const mxy_names[] = { "0", "h", "v", "hv" };
+static const char *const scaled_paths[] = { "", "_dy1", "_dy2" };
+
+static int mc_h_next(const int h) {
+ switch (h) {
+ case 4:
+ case 8:
+ case 16:
+ return (h * 3) >> 1;
+ case 6:
+ case 12:
+ case 24:
+ return (h & (h - 1)) * 2;
+ default:
+ return h * 2;
+ }
+}
+
+static void check_mc(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 135 * 135,);
+ PIXEL_RECT(c_dst, 128, 128);
+ PIXEL_RECT(a_dst, 128, 128);
+ const pixel *src = src_buf + 135 * 3 + 3;
+ const ptrdiff_t src_stride = 135 * sizeof(pixel);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
+ ptrdiff_t src_stride, int w, int h, int mx, int my
+ HIGHBD_DECL_SUFFIX);
+
+ for (int filter = 0; filter < N_2D_FILTERS; filter++)
+ for (int w = 2; w <= 128; w <<= 1) {
+ for (int mxy = 0; mxy < 4; mxy++)
+ if (check_func(c->mc[filter], "mc_%s_w%d_%s_%dbpc",
+ filter_names[filter], w, mxy_names[mxy], BITDEPTH))
+ {
+ const int h_min = w <= 32 ? 2 : w / 4;
+ const int h_max = imax(imin(w * 4, 128), 32);
+ for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
+ const int mx = (mxy & 1) ? rnd() % 15 + 1 : 0;
+ const int my = (mxy & 2) ? rnd() % 15 + 1 : 0;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < 135 * 135; i++)
+ src_buf[i] = rnd() & bitdepth_max;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride,
+ a_dst, a_dst_stride,
+ w, h, "dst");
+
+ if (filter == FILTER_2D_8TAP_REGULAR ||
+ filter == FILTER_2D_BILINEAR)
+ {
+ bench_new(a_dst, a_dst_stride, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+ }
+ report("mc");
+}
+
+/* Generate worst case input in the topleft corner, randomize the rest */
+static void generate_mct_input(pixel *const buf, const int bitdepth_max) {
+ static const int8_t pattern[8] = { -1, 0, -1, 0, 0, -1, 0, -1 };
+ const int sign = -(rnd() & 1);
+
+ for (int y = 0; y < 135; y++)
+ for (int x = 0; x < 135; x++)
+ buf[135*y+x] = ((x | y) < 8 ? (pattern[x] ^ pattern[y] ^ sign)
+ : rnd()) & bitdepth_max;
+}
+
+static void check_mct(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 135 * 135,);
+ ALIGN_STK_64(int16_t, c_tmp, 128 * 128,);
+ ALIGN_STK_64(int16_t, a_tmp, 128 * 128,);
+ const pixel *src = src_buf + 135 * 3 + 3;
+ const ptrdiff_t src_stride = 135 * sizeof(pixel);
+
+ declare_func(void, int16_t *tmp, const pixel *src, ptrdiff_t src_stride,
+ int w, int h, int mx, int my HIGHBD_DECL_SUFFIX);
+
+ for (int filter = 0; filter < N_2D_FILTERS; filter++)
+ for (int w = 4; w <= 128; w <<= 1)
+ for (int mxy = 0; mxy < 4; mxy++)
+ if (check_func(c->mct[filter], "mct_%s_w%d_%s_%dbpc",
+ filter_names[filter], w, mxy_names[mxy], BITDEPTH))
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
+ {
+ const int mx = (mxy & 1) ? rnd() % 15 + 1 : 0;
+ const int my = (mxy & 2) ? rnd() % 15 + 1 : 0;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ generate_mct_input(src_buf, bitdepth_max);
+
+ call_ref(c_tmp, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ call_new(a_tmp, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ checkasm_check(int16_t, c_tmp, w * sizeof(*c_tmp),
+ a_tmp, w * sizeof(*a_tmp),
+ w, h, "tmp");
+
+ if (filter == FILTER_2D_8TAP_REGULAR ||
+ filter == FILTER_2D_BILINEAR)
+ {
+ bench_new(a_tmp, src, src_stride, w, h,
+ mx, my HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("mct");
+}
+
+static void check_mc_scaled(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 263 * 263,);
+ PIXEL_RECT(c_dst, 128, 128);
+ PIXEL_RECT(a_dst, 128, 128);
+ const pixel *src = src_buf + 263 * 3 + 3;
+ const ptrdiff_t src_stride = 263 * sizeof(pixel);
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
+ ptrdiff_t src_stride, int w, int h,
+ int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX);
+
+ for (int filter = 0; filter < N_2D_FILTERS; filter++)
+ for (int w = 2; w <= 128; w <<= 1) {
+ for (int p = 0; p < 3; ++p) {
+ if (check_func(c->mc_scaled[filter], "mc_scaled_%s_w%d%s_%dbpc",
+ filter_names[filter], w, scaled_paths[p], BITDEPTH))
+ {
+ const int h_min = w <= 32 ? 2 : w / 4;
+ const int h_max = imax(imin(w * 4, 128), 32);
+ for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
+ const int mx = rnd() % 1024;
+ const int my = rnd() % 1024;
+ const int dx = rnd() % 2048 + 1;
+ const int dy = !p
+ ? rnd() % 2048 + 1
+ : p << 10; // ystep=1.0 and ystep=2.0 paths
+
+ for (int k = 0; k < 263 * 263; k++)
+ src_buf[k] = rnd() & bitdepth_max;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride,
+ a_dst, a_dst_stride,
+ w, h, "dst");
+
+ if (filter == FILTER_2D_8TAP_REGULAR ||
+ filter == FILTER_2D_BILINEAR)
+ bench_new(a_dst, a_dst_stride, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+ }
+ report("mc_scaled");
+}
+
+static void check_mct_scaled(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 263 * 263,);
+ ALIGN_STK_64(int16_t, c_tmp, 128 * 128,);
+ ALIGN_STK_64(int16_t, a_tmp, 128 * 128,);
+ const pixel *src = src_buf + 263 * 3 + 3;
+ const ptrdiff_t src_stride = 263 * sizeof(pixel);
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ declare_func(void, int16_t *tmp, const pixel *src, ptrdiff_t src_stride,
+ int w, int h, int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX);
+
+ for (int filter = 0; filter < N_2D_FILTERS; filter++)
+ for (int w = 4; w <= 128; w <<= 1)
+ for (int p = 0; p < 3; ++p) {
+ if (check_func(c->mct_scaled[filter], "mct_scaled_%s_w%d%s_%dbpc",
+ filter_names[filter], w, scaled_paths[p], BITDEPTH))
+ {
+ const int h_min = imax(w / 4, 4);
+ const int h_max = imin(w * 4, 128);
+ for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
+ const int mx = rnd() % 1024;
+ const int my = rnd() % 1024;
+ const int dx = rnd() % 2048 + 1;
+ const int dy = !p
+ ? rnd() % 2048 + 1
+ : p << 10; // ystep=1.0 and ystep=2.0 paths
+
+ for (int k = 0; k < 263 * 263; k++)
+ src_buf[k] = rnd() & bitdepth_max;
+
+ call_ref(c_tmp, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ call_new(a_tmp, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ checkasm_check(int16_t, c_tmp, w * sizeof(*c_tmp),
+ a_tmp, w * sizeof(*a_tmp),
+ w, h, "tmp");
+
+ if (filter == FILTER_2D_8TAP_REGULAR ||
+ filter == FILTER_2D_BILINEAR)
+ bench_new(a_tmp, src, src_stride,
+ w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ }
+ report("mct_scaled");
+}
+
+static void init_tmp(Dav1dMCDSPContext *const c, pixel *const buf,
+ int16_t (*const tmp)[128 * 128], const int bitdepth_max)
+{
+ for (int i = 0; i < 2; i++) {
+ generate_mct_input(buf, bitdepth_max);
+ c->mct[FILTER_2D_8TAP_SHARP](tmp[i], buf + 135 * 3 + 3,
+ 135 * sizeof(pixel), 128, 128,
+ 8, 8 HIGHBD_TAIL_SUFFIX);
+ }
+}
+
+static void check_avg(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
+ PIXEL_RECT(c_dst, 135, 135);
+ PIXEL_RECT(a_dst, 128, 128);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
+ const int16_t *tmp2, int w, int h HIGHBD_DECL_SUFFIX);
+
+ for (int w = 4; w <= 128; w <<= 1)
+ if (check_func(c->avg, "avg_w%d_%dbpc", w, BITDEPTH)) {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
+ {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ init_tmp(c, c_dst, tmp, bitdepth_max);
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("avg");
+}
+
+static void check_w_avg(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
+ PIXEL_RECT(c_dst, 135, 135);
+ PIXEL_RECT(a_dst, 128, 128);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
+ const int16_t *tmp2, int w, int h, int weight HIGHBD_DECL_SUFFIX);
+
+ for (int w = 4; w <= 128; w <<= 1)
+ if (check_func(c->w_avg, "w_avg_w%d_%dbpc", w, BITDEPTH)) {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
+ {
+ int weight = rnd() % 15 + 1;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ init_tmp(c, c_dst, tmp, bitdepth_max);
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("w_avg");
+}
+
+static void check_mask(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
+ PIXEL_RECT(c_dst, 135, 135);
+ PIXEL_RECT(a_dst, 128, 128);
+ ALIGN_STK_64(uint8_t, mask, 128 * 128,);
+
+ for (int i = 0; i < 128 * 128; i++)
+ mask[i] = rnd() % 65;
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
+ const int16_t *tmp2, int w, int h, const uint8_t *mask
+ HIGHBD_DECL_SUFFIX);
+
+ for (int w = 4; w <= 128; w <<= 1)
+ if (check_func(c->mask, "mask_w%d_%dbpc", w, BITDEPTH)) {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
+ {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ init_tmp(c, c_dst, tmp, bitdepth_max);
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("mask");
+}
+
+static void check_w_mask(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
+ PIXEL_RECT(c_dst, 135, 135);
+ PIXEL_RECT(a_dst, 128, 128);
+ ALIGN_STK_64(uint8_t, c_mask, 128 * 128,);
+ ALIGN_STK_64(uint8_t, a_mask, 128 * 128,);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
+ const int16_t *tmp2, int w, int h, uint8_t *mask, int sign
+ HIGHBD_DECL_SUFFIX);
+
+ static const uint16_t ss[] = { 444, 422, 420 };
+ static const uint8_t ss_hor[] = { 0, 1, 1 };
+ static const uint8_t ss_ver[] = { 0, 0, 1 };
+
+ for (int i = 0; i < 3; i++)
+ for (int w = 4; w <= 128; w <<= 1)
+ if (check_func(c->w_mask[i], "w_mask_%d_w%d_%dbpc", ss[i], w,
+ BITDEPTH))
+ {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
+ {
+ int sign = rnd() & 1;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ init_tmp(c, c_dst, tmp, bitdepth_max);
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, tmp[0], tmp[1], w, h,
+ c_mask, sign HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h,
+ a_mask, sign HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride,
+ a_dst, a_dst_stride,
+ w, h, "dst");
+ checkasm_check(uint8_t, c_mask, w >> ss_hor[i],
+ a_mask, w >> ss_hor[i],
+ w >> ss_hor[i], h >> ss_ver[i],
+ "mask");
+
+ bench_new(a_dst, a_dst_stride, tmp[0], tmp[1], w, h,
+ a_mask, sign HIGHBD_TAIL_SUFFIX);
+ }
+ }
+ report("w_mask");
+}
+
+static void check_blend(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, tmp, 32 * 32,);
+ PIXEL_RECT(c_dst, 32, 32);
+ PIXEL_RECT(a_dst, 32, 32);
+ ALIGN_STK_64(uint8_t, mask, 32 * 32,);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
+ int w, int h, const uint8_t *mask);
+
+ for (int w = 4; w <= 32; w <<= 1) {
+ if (check_func(c->blend, "blend_w%d_%dbpc", w, BITDEPTH))
+ for (int h = imax(w / 2, 4); h <= imin(w * 2, 32); h <<= 1) {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ for (int i = 0; i < 32 * 32; i++) {
+ tmp[i] = rnd() & bitdepth_max;
+ mask[i] = rnd() % 65;
+ }
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ c_dst[y*PXSTRIDE(c_dst_stride) + x] =
+ a_dst[y*PXSTRIDE(a_dst_stride) + x] = rnd() & bitdepth_max;
+
+ call_ref(c_dst, c_dst_stride, tmp, w, h, mask);
+ call_new(a_dst, a_dst_stride, tmp, w, h, mask);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(alternate(c_dst, a_dst), a_dst_stride, tmp, w, h, mask);
+ }
+ }
+ report("blend");
+}
+
+static void check_blend_v(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, tmp, 32 * 128,);
+ PIXEL_RECT(c_dst, 32, 128);
+ PIXEL_RECT(a_dst, 32, 128);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
+ int w, int h);
+
+ for (int w = 2; w <= 32; w <<= 1) {
+ if (check_func(c->blend_v, "blend_v_w%d_%dbpc", w, BITDEPTH))
+ for (int h = 2; h <= (w == 2 ? 64 : 128); h <<= 1) {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ c_dst[y*PXSTRIDE(c_dst_stride) + x] =
+ a_dst[y*PXSTRIDE(a_dst_stride) + x] = rnd() & bitdepth_max;
+
+ for (int i = 0; i < 32 * 128; i++)
+ tmp[i] = rnd() & bitdepth_max;
+
+ call_ref(c_dst, c_dst_stride, tmp, w, h);
+ call_new(a_dst, a_dst_stride, tmp, w, h);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(alternate(c_dst, a_dst), a_dst_stride, tmp, w, h);
+ }
+ }
+ report("blend_v");
+}
+
+static void check_blend_h(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, tmp, 128 * 32,);
+ PIXEL_RECT(c_dst, 128, 32);
+ PIXEL_RECT(a_dst, 128, 32);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
+ int w, int h);
+
+ for (int w = 2; w <= 128; w <<= 1) {
+ if (check_func(c->blend_h, "blend_h_w%d_%dbpc", w, BITDEPTH))
+ for (int h = (w == 128 ? 4 : 2); h <= 32; h <<= 1) {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ for (int y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
+ c_dst[y*PXSTRIDE(c_dst_stride) + x] =
+ a_dst[y*PXSTRIDE(a_dst_stride) + x] = rnd() & bitdepth_max;
+
+ for (int i = 0; i < 128 * 32; i++)
+ tmp[i] = rnd() & bitdepth_max;
+
+ call_ref(c_dst, c_dst_stride, tmp, w, h);
+ call_new(a_dst, a_dst_stride, tmp, w, h);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ w, h, "dst");
+
+ bench_new(alternate(c_dst, a_dst), a_dst_stride, tmp, w, h);
+ }
+ }
+ report("blend_h");
+}
+
+static void check_warp8x8(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 15 * 15,);
+ PIXEL_RECT(c_dst, 8, 8);
+ PIXEL_RECT(a_dst, 8, 8);
+ int16_t abcd[4];
+ const pixel *src = src_buf + 15 * 3 + 3;
+ const ptrdiff_t src_stride = 15 * sizeof(pixel);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
+ ptrdiff_t src_stride, const int16_t *abcd, int mx, int my
+ HIGHBD_DECL_SUFFIX);
+
+ if (check_func(c->warp8x8, "warp_8x8_%dbpc", BITDEPTH)) {
+ const int mx = (rnd() & 0x1fff) - 0xa00;
+ const int my = (rnd() & 0x1fff) - 0xa00;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < 4; i++)
+ abcd[i] = (rnd() & 0x1fff) - 0xa00;
+
+ for (int i = 0; i < 15 * 15; i++)
+ src_buf[i] = rnd() & bitdepth_max;
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ 8, 8, "dst");
+
+ bench_new(a_dst, a_dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ }
+ report("warp8x8");
+}
+
+static void check_warp8x8t(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, src_buf, 15 * 15,);
+ ALIGN_STK_64(int16_t, c_tmp, 8 * 8,);
+ ALIGN_STK_64(int16_t, a_tmp, 8 * 8,);
+ int16_t abcd[4];
+ const pixel *src = src_buf + 15 * 3 + 3;
+ const ptrdiff_t src_stride = 15 * sizeof(pixel);
+
+ declare_func(void, int16_t *tmp, ptrdiff_t tmp_stride, const pixel *src,
+ ptrdiff_t src_stride, const int16_t *abcd, int mx, int my
+ HIGHBD_DECL_SUFFIX);
+
+ if (check_func(c->warp8x8t, "warp_8x8t_%dbpc", BITDEPTH)) {
+ const int mx = (rnd() & 0x1fff) - 0xa00;
+ const int my = (rnd() & 0x1fff) - 0xa00;
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < 4; i++)
+ abcd[i] = (rnd() & 0x1fff) - 0xa00;
+
+ for (int i = 0; i < 15 * 15; i++)
+ src_buf[i] = rnd() & bitdepth_max;
+
+ call_ref(c_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ call_new(a_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ checkasm_check(int16_t, c_tmp, 8 * sizeof(*c_tmp),
+ a_tmp, 8 * sizeof(*a_tmp),
+ 8, 8, "tmp");
+
+ bench_new(a_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
+ }
+ report("warp8x8t");
+}
+
+enum EdgeFlags {
+ HAVE_TOP = 1,
+ HAVE_BOTTOM = 2,
+ HAVE_LEFT = 4,
+ HAVE_RIGHT = 8,
+};
+
+static void random_offset_for_edge(int *const x, int *const y,
+ const int bw, const int bh,
+ int *const iw, int *const ih,
+ const enum EdgeFlags edge)
+{
+#define set_off(edge1, edge2, pos, dim) \
+ *i##dim = edge & (HAVE_##edge1 | HAVE_##edge2) ? 160 : 1 + (rnd() % (b##dim - 2)); \
+ switch (edge & (HAVE_##edge1 | HAVE_##edge2)) { \
+ case HAVE_##edge1 | HAVE_##edge2: \
+ assert(b##dim <= *i##dim); \
+ *pos = rnd() % (*i##dim - b##dim + 1); \
+ break; \
+ case HAVE_##edge1: \
+ *pos = (*i##dim - b##dim) + 1 + (rnd() % (b##dim - 1)); \
+ break; \
+ case HAVE_##edge2: \
+ *pos = -(1 + (rnd() % (b##dim - 1))); \
+ break; \
+ case 0: \
+ assert(b##dim - 1 > *i##dim); \
+ *pos = -(1 + (rnd() % (b##dim - *i##dim - 1))); \
+ break; \
+ }
+ set_off(LEFT, RIGHT, x, w);
+ set_off(TOP, BOTTOM, y, h);
+}
+
+static void check_emuedge(Dav1dMCDSPContext *const c) {
+ ALIGN_STK_64(pixel, c_dst, 135 * 192,);
+ ALIGN_STK_64(pixel, a_dst, 135 * 192,);
+ ALIGN_STK_64(pixel, src, 160 * 160,);
+
+ for (int i = 0; i < 160 * 160; i++)
+ src[i] = rnd() & ((1U << BITDEPTH) - 1);
+
+ declare_func(void, intptr_t bw, intptr_t bh, intptr_t iw, intptr_t ih,
+ intptr_t x, intptr_t y,
+ pixel *dst, ptrdiff_t dst_stride,
+ const pixel *src, ptrdiff_t src_stride);
+
+ int x, y, iw, ih;
+ for (int w = 4; w <= 128; w <<= 1)
+ if (check_func(c->emu_edge, "emu_edge_w%d_%dbpc", w, BITDEPTH)) {
+ for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1) {
+ // we skip 0xf, since it implies that we don't need emu_edge
+ for (enum EdgeFlags edge = 0; edge < 0xf; edge++) {
+ const int bw = w + (rnd() & 7);
+ const int bh = h + (rnd() & 7);
+ random_offset_for_edge(&x, &y, bw, bh, &iw, &ih, edge);
+ call_ref(bw, bh, iw, ih, x, y,
+ c_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
+ call_new(bw, bh, iw, ih, x, y,
+ a_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
+ checkasm_check_pixel(c_dst, 192 * sizeof(pixel),
+ a_dst, 192 * sizeof(pixel),
+ bw, bh, "dst");
+ }
+ }
+ for (enum EdgeFlags edge = 1; edge < 0xf; edge <<= 1) {
+ random_offset_for_edge(&x, &y, w + 7, w + 7, &iw, &ih, edge);
+ bench_new(w + 7, w + 7, iw, ih, x, y,
+ a_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
+ }
+ }
+ report("emu_edge");
+}
+
+static int get_upscale_x0(const int in_w, const int out_w, const int step) {
+ const int err = out_w * step - (in_w << 14);
+ const int x0 = (-((out_w - in_w) << 13) + (out_w >> 1)) / out_w + 128 - (err >> 1);
+ return x0 & 0x3fff;
+}
+
+static void check_resize(Dav1dMCDSPContext *const c) {
+ PIXEL_RECT(c_dst, 1024, 64);
+ PIXEL_RECT(a_dst, 1024, 64);
+ ALIGN_STK_64(pixel, src, 512 * 64,);
+
+ const int height = 64;
+ const int max_src_width = 512;
+ const ptrdiff_t src_stride = 512 * sizeof(pixel);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride,
+ const pixel *src, ptrdiff_t src_stride,
+ int dst_w, int src_w, int h, int dx, int mx0
+ HIGHBD_DECL_SUFFIX);
+
+ if (check_func(c->resize, "resize_%dbpc", BITDEPTH)) {
+#if BITDEPTH == 16
+ const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
+#else
+ const int bitdepth_max = 0xff;
+#endif
+
+ for (int i = 0; i < max_src_width * height; i++)
+ src[i] = rnd() & bitdepth_max;
+
+ const int w_den = 9 + (rnd() & 7);
+ const int src_w = 16 + (rnd() % (max_src_width - 16 + 1));
+ const int dst_w = w_den * src_w >> 3;
+#define scale_fac(ref_sz, this_sz) \
+ ((((ref_sz) << 14) + ((this_sz) >> 1)) / (this_sz))
+ const int dx = scale_fac(src_w, dst_w);
+#undef scale_fac
+ const int mx0 = get_upscale_x0(src_w, dst_w, dx);
+
+ CLEAR_PIXEL_RECT(c_dst);
+ CLEAR_PIXEL_RECT(a_dst);
+
+ call_ref(c_dst, c_dst_stride, src, src_stride,
+ dst_w, height, src_w, dx, mx0 HIGHBD_TAIL_SUFFIX);
+ call_new(a_dst, a_dst_stride, src, src_stride,
+ dst_w, height, src_w, dx, mx0 HIGHBD_TAIL_SUFFIX);
+ checkasm_check_pixel_padded_align(c_dst, c_dst_stride, a_dst, a_dst_stride,
+ dst_w, height, "dst", 16, 1);
+
+ bench_new(a_dst, a_dst_stride, src, src_stride,
+ 512, height, 512 * 8 / w_den, dx, mx0 HIGHBD_TAIL_SUFFIX);
+ }
+
+ report("resize");
+}
+
+void bitfn(checkasm_check_mc)(void) {
+ Dav1dMCDSPContext c;
+ bitfn(dav1d_mc_dsp_init)(&c);
+
+ check_mc(&c);
+ check_mct(&c);
+ check_mc_scaled(&c);
+ check_mct_scaled(&c);
+ check_avg(&c);
+ check_w_avg(&c);
+ check_mask(&c);
+ check_w_mask(&c);
+ check_blend(&c);
+ check_blend_v(&c);
+ check_blend_h(&c);
+ check_warp8x8(&c);
+ check_warp8x8t(&c);
+ check_emuedge(&c);
+ check_resize(&c);
+}
diff --git a/third_party/dav1d/tests/checkasm/msac.c b/third_party/dav1d/tests/checkasm/msac.c
new file mode 100644
index 0000000000..b9c89b47cf
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/msac.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2019, VideoLAN and dav1d authors
+ * Copyright © 2019, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+
+#include "src/cpu.h"
+#include "src/msac.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define BUF_SIZE 8192
+
+/* The normal code doesn't use function pointers */
+typedef unsigned (*decode_symbol_adapt_fn)(MsacContext *s, uint16_t *cdf,
+ size_t n_symbols);
+typedef unsigned (*decode_adapt_fn)(MsacContext *s, uint16_t *cdf);
+typedef unsigned (*decode_bool_equi_fn)(MsacContext *s);
+typedef unsigned (*decode_bool_fn)(MsacContext *s, unsigned f);
+
+typedef struct {
+ decode_symbol_adapt_fn decode_symbol_adapt4;
+ decode_symbol_adapt_fn decode_symbol_adapt8;
+ decode_symbol_adapt_fn decode_symbol_adapt16;
+ decode_adapt_fn decode_bool_adapt;
+ decode_bool_equi_fn decode_bool_equi;
+ decode_bool_fn decode_bool;
+ decode_adapt_fn decode_hi_tok;
+} MsacDSPContext;
+
+static void randomize_cdf(uint16_t *const cdf, const int n) {
+ int i;
+ for (i = 15; i > n; i--)
+ cdf[i] = rnd(); // padding
+ cdf[i] = 0; // count
+ do {
+ cdf[i - 1] = cdf[i] + rnd() % (32768 - cdf[i] - i) + 1;
+ } while (--i > 0);
+}
+
+/* memcmp() on structs can have weird behavior due to padding etc. */
+static int msac_cmp(const MsacContext *const a, const MsacContext *const b) {
+ return a->buf_pos != b->buf_pos || a->buf_end != b->buf_end ||
+ a->dif != b->dif || a->rng != b->rng || a->cnt != b->cnt ||
+ a->allow_update_cdf != b->allow_update_cdf;
+}
+
+static void msac_dump(unsigned c_res, unsigned a_res,
+ const MsacContext *const a, const MsacContext *const b,
+ const uint16_t *const cdf_a, const uint16_t *const cdf_b,
+ const int num_cdf)
+{
+ if (c_res != a_res)
+ fprintf(stderr, "c_res %u a_res %u\n", c_res, a_res);
+ if (a->buf_pos != b->buf_pos)
+ fprintf(stderr, "buf_pos %p vs %p\n", a->buf_pos, b->buf_pos);
+ if (a->buf_end != b->buf_end)
+ fprintf(stderr, "buf_end %p vs %p\n", a->buf_end, b->buf_end);
+ if (a->dif != b->dif)
+ fprintf(stderr, "dif %zx vs %zx\n", a->dif, b->dif);
+ if (a->rng != b->rng)
+ fprintf(stderr, "rng %u vs %u\n", a->rng, b->rng);
+ if (a->cnt != b->cnt)
+ fprintf(stderr, "cnt %d vs %d\n", a->cnt, b->cnt);
+ if (a->allow_update_cdf)
+ fprintf(stderr, "allow_update_cdf %d vs %d\n",
+ a->allow_update_cdf, b->allow_update_cdf);
+ if (num_cdf && memcmp(cdf_a, cdf_b, sizeof(*cdf_a) * (num_cdf + 1))) {
+ fprintf(stderr, "cdf:\n");
+ for (int i = 0; i <= num_cdf; i++)
+ fprintf(stderr, " %5u", cdf_a[i]);
+ fprintf(stderr, "\n");
+ for (int i = 0; i <= num_cdf; i++)
+ fprintf(stderr, " %5u", cdf_b[i]);
+ fprintf(stderr, "\n");
+ for (int i = 0; i <= num_cdf; i++)
+ fprintf(stderr, " %c", cdf_a[i] != cdf_b[i] ? 'x' : '.');
+ fprintf(stderr, "\n");
+ }
+}
+
+#define CHECK_SYMBOL_ADAPT(n, n_min, n_max) do { \
+ if (check_func(c->decode_symbol_adapt##n, \
+ "msac_decode_symbol_adapt%d", n)) \
+ { \
+ for (int cdf_update = 0; cdf_update <= 1; cdf_update++) { \
+ for (int ns = n_min; ns <= n_max; ns++) { \
+ dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update); \
+ s_a = s_c; \
+ randomize_cdf(cdf[0], ns); \
+ memcpy(cdf[1], cdf[0], sizeof(*cdf)); \
+ for (int i = 0; i < 64; i++) { \
+ unsigned c_res = call_ref(&s_c, cdf[0], ns); \
+ unsigned a_res = call_new(&s_a, cdf[1], ns); \
+ if (c_res != a_res || msac_cmp(&s_c, &s_a) || \
+ memcmp(cdf[0], cdf[1], sizeof(**cdf) * (ns + 1))) \
+ { \
+ if (fail()) \
+ msac_dump(c_res, a_res, &s_c, &s_a, \
+ cdf[0], cdf[1], ns); \
+ } \
+ } \
+ if (cdf_update && ns == n - 1) \
+ bench_new(alternate(&s_c, &s_a), \
+ alternate(cdf[0], cdf[1]), ns); \
+ } \
+ } \
+ } \
+} while (0)
+
+static void check_decode_symbol(MsacDSPContext *const c, uint8_t *const buf) {
+ ALIGN_STK_32(uint16_t, cdf, 2, [16]);
+ MsacContext s_c, s_a;
+
+ declare_func(unsigned, MsacContext *s, uint16_t *cdf, size_t n_symbols);
+ CHECK_SYMBOL_ADAPT( 4, 1, 4);
+ CHECK_SYMBOL_ADAPT( 8, 1, 7);
+ CHECK_SYMBOL_ADAPT(16, 3, 15);
+ report("decode_symbol");
+}
+
+static void check_decode_bool_adapt(MsacDSPContext *const c, uint8_t *const buf) {
+ MsacContext s_c, s_a;
+
+ declare_func(unsigned, MsacContext *s, uint16_t *cdf);
+ if (check_func(c->decode_bool_adapt, "msac_decode_bool_adapt")) {
+ uint16_t cdf[2][2];
+ for (int cdf_update = 0; cdf_update <= 1; cdf_update++) {
+ dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update);
+ s_a = s_c;
+ cdf[0][0] = cdf[1][0] = rnd() % 32767 + 1;
+ cdf[0][1] = cdf[1][1] = 0;
+ for (int i = 0; i < 64; i++) {
+ unsigned c_res = call_ref(&s_c, cdf[0]);
+ unsigned a_res = call_new(&s_a, cdf[1]);
+ if (c_res != a_res || msac_cmp(&s_c, &s_a) ||
+ memcmp(cdf[0], cdf[1], sizeof(*cdf)))
+ {
+ if (fail())
+ msac_dump(c_res, a_res, &s_c, &s_a, cdf[0], cdf[1], 1);
+ }
+ }
+ if (cdf_update)
+ bench_new(alternate(&s_c, &s_a), alternate(cdf[0], cdf[1]));
+ }
+ }
+}
+
+static void check_decode_bool_equi(MsacDSPContext *const c, uint8_t *const buf) {
+ MsacContext s_c, s_a;
+
+ declare_func(unsigned, MsacContext *s);
+ if (check_func(c->decode_bool_equi, "msac_decode_bool_equi")) {
+ dav1d_msac_init(&s_c, buf, BUF_SIZE, 1);
+ s_a = s_c;
+ for (int i = 0; i < 64; i++) {
+ unsigned c_res = call_ref(&s_c);
+ unsigned a_res = call_new(&s_a);
+ if (c_res != a_res || msac_cmp(&s_c, &s_a)) {
+ if (fail())
+ msac_dump(c_res, a_res, &s_c, &s_a, NULL, NULL, 0);
+ }
+ }
+ bench_new(alternate(&s_c, &s_a));
+ }
+}
+
+static void check_decode_bool(MsacDSPContext *const c, uint8_t *const buf) {
+ MsacContext s_c, s_a;
+
+ declare_func(unsigned, MsacContext *s, unsigned f);
+ if (check_func(c->decode_bool, "msac_decode_bool")) {
+ dav1d_msac_init(&s_c, buf, BUF_SIZE, 1);
+ s_a = s_c;
+ for (int i = 0; i < 64; i++) {
+ const unsigned f = rnd() & 0x7fff;
+ unsigned c_res = call_ref(&s_c, f);
+ unsigned a_res = call_new(&s_a, f);
+ if (c_res != a_res || msac_cmp(&s_c, &s_a)) {
+ if (fail())
+ msac_dump(c_res, a_res, &s_c, &s_a, NULL, NULL, 0);
+ }
+ }
+ bench_new(alternate(&s_c, &s_a), 16384);
+ }
+
+}
+
+static void check_decode_bool_funcs(MsacDSPContext *const c, uint8_t *const buf) {
+ check_decode_bool_adapt(c, buf);
+ check_decode_bool_equi(c, buf);
+ check_decode_bool(c, buf);
+ report("decode_bool");
+}
+
+static void check_decode_hi_tok(MsacDSPContext *const c, uint8_t *const buf) {
+ ALIGN_STK_16(uint16_t, cdf, 2, [16]);
+ MsacContext s_c, s_a;
+
+ declare_func(unsigned, MsacContext *s, uint16_t *cdf);
+ if (check_func(c->decode_hi_tok, "msac_decode_hi_tok")) {
+ for (int cdf_update = 0; cdf_update <= 1; cdf_update++) {
+ dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update);
+ s_a = s_c;
+ randomize_cdf(cdf[0], 3);
+ memcpy(cdf[1], cdf[0], sizeof(*cdf));
+ for (int i = 0; i < 64; i++) {
+ unsigned c_res = call_ref(&s_c, cdf[0]);
+ unsigned a_res = call_new(&s_a, cdf[1]);
+ if (c_res != a_res || msac_cmp(&s_c, &s_a) ||
+ memcmp(cdf[0], cdf[1], sizeof(*cdf)))
+ {
+ if (fail())
+ msac_dump(c_res, a_res, &s_c, &s_a, cdf[0], cdf[1], 3);
+ break;
+ }
+ }
+ if (cdf_update)
+ bench_new(alternate(&s_c, &s_a), alternate(cdf[0], cdf[1]));
+ }
+ }
+ report("decode_hi_tok");
+}
+
+void checkasm_check_msac(void) {
+ MsacDSPContext c;
+ c.decode_symbol_adapt4 = dav1d_msac_decode_symbol_adapt_c;
+ c.decode_symbol_adapt8 = dav1d_msac_decode_symbol_adapt_c;
+ c.decode_symbol_adapt16 = dav1d_msac_decode_symbol_adapt_c;
+ c.decode_bool_adapt = dav1d_msac_decode_bool_adapt_c;
+ c.decode_bool_equi = dav1d_msac_decode_bool_equi_c;
+ c.decode_bool = dav1d_msac_decode_bool_c;
+ c.decode_hi_tok = dav1d_msac_decode_hi_tok_c;
+
+#if (ARCH_AARCH64 || ARCH_ARM) && HAVE_ASM
+ if (dav1d_get_cpu_flags() & DAV1D_ARM_CPU_FLAG_NEON) {
+ c.decode_symbol_adapt4 = dav1d_msac_decode_symbol_adapt4_neon;
+ c.decode_symbol_adapt8 = dav1d_msac_decode_symbol_adapt8_neon;
+ c.decode_symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_neon;
+ c.decode_bool_adapt = dav1d_msac_decode_bool_adapt_neon;
+ c.decode_bool_equi = dav1d_msac_decode_bool_equi_neon;
+ c.decode_bool = dav1d_msac_decode_bool_neon;
+ c.decode_hi_tok = dav1d_msac_decode_hi_tok_neon;
+ }
+#elif ARCH_X86 && HAVE_ASM
+ if (dav1d_get_cpu_flags() & DAV1D_X86_CPU_FLAG_SSE2) {
+ c.decode_symbol_adapt4 = dav1d_msac_decode_symbol_adapt4_sse2;
+ c.decode_symbol_adapt8 = dav1d_msac_decode_symbol_adapt8_sse2;
+ c.decode_symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_sse2;
+ c.decode_bool_adapt = dav1d_msac_decode_bool_adapt_sse2;
+ c.decode_bool_equi = dav1d_msac_decode_bool_equi_sse2;
+ c.decode_bool = dav1d_msac_decode_bool_sse2;
+ c.decode_hi_tok = dav1d_msac_decode_hi_tok_sse2;
+ }
+
+#if ARCH_X86_64
+ if (dav1d_get_cpu_flags() & DAV1D_X86_CPU_FLAG_AVX2) {
+ c.decode_symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_avx2;
+ }
+#endif
+#endif
+
+ uint8_t buf[BUF_SIZE];
+ for (int i = 0; i < BUF_SIZE; i++)
+ buf[i] = rnd();
+
+ check_decode_symbol(&c, buf);
+ check_decode_bool_funcs(&c, buf);
+ check_decode_hi_tok(&c, buf);
+}
diff --git a/third_party/dav1d/tests/checkasm/refmvs.c b/third_party/dav1d/tests/checkasm/refmvs.c
new file mode 100644
index 0000000000..f21c81f85a
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/refmvs.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright © 2021, VideoLAN and dav1d authors
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests/checkasm/checkasm.h"
+#include "src/refmvs.h"
+
+#include <stdio.h>
+
+static inline int gen_mv(const int total_bits, int spel_bits) {
+ int bits = rnd() & ((1 << spel_bits) - 1);
+ do {
+ bits |= (rnd() & 1) << spel_bits;
+ } while (rnd() & 1 && ++spel_bits < total_bits);
+ // the do/while makes it relatively more likely to be close to zero (fpel)
+ // than far away
+ return rnd() & 1 ? -bits : bits;
+}
+
+static void check_save_tmvs(const Dav1dRefmvsDSPContext *const c) {
+ refmvs_block *rr[31];
+ refmvs_block r[31 * 256];
+ ALIGN_STK_64(refmvs_temporal_block, c_rp, 128 * 16,);
+ ALIGN_STK_64(refmvs_temporal_block, a_rp, 128 * 16,);
+ uint8_t ref_sign[7];
+
+ for (int i = 0; i < 31; i++)
+ rr[i] = &r[i * 256];
+
+ declare_func(void, refmvs_temporal_block *rp, const ptrdiff_t stride,
+ refmvs_block *const *const rr, const uint8_t *const ref_sign,
+ int col_end8, int row_end8, int col_start8, int row_start8);
+
+ if (check_func(c->save_tmvs, "save_tmvs")) {
+ const int row_start8 = rnd() & 7;
+ const int row_end8 = 8 + (rnd() & 7);
+ const int col_start8 = rnd() & 31;
+ const int col_end8 = 96 + (rnd() & 31);
+
+ for (int i = 0; i < 7; i++)
+ ref_sign[i] = rnd() & 1;
+
+ for (int i = row_start8; i < row_end8; i++)
+ for (int j = col_start8; j < col_end8;) {
+ int bs = rnd() % N_BS_SIZES;
+ while (j + ((dav1d_block_dimensions[bs][0] + 1) >> 1) > col_end8)
+ bs++;
+ rr[i * 2][j * 2 + 1] = (refmvs_block) {
+ .mv.mv[0].x = gen_mv(14, 10),
+ .mv.mv[0].y = gen_mv(14, 10),
+ .mv.mv[1].x = gen_mv(14, 10),
+ .mv.mv[1].y = gen_mv(14, 10),
+ .ref.ref = { (rnd() % 9) - 1, (rnd() % 9) - 1 },
+ .bs = bs
+ };
+ for (int k = 0; k < (dav1d_block_dimensions[bs][0] + 1) >> 1; k++, j++) {
+ c_rp[i * 128 + j].mv.n = 0xdeadbeef;
+ c_rp[i * 128 + j].ref = 0xdd;
+ }
+ }
+
+ call_ref(c_rp + row_start8 * 128, 128, rr, ref_sign,
+ col_end8, row_end8, col_start8, row_start8);
+ call_new(a_rp + row_start8 * 128, 128, rr, ref_sign,
+ col_end8, row_end8, col_start8, row_start8);
+ for (int i = row_start8; i < row_end8; i++)
+ for (int j = col_start8; j < col_end8; j++)
+ if (c_rp[i * 128 + j].mv.n != a_rp[i * 128 + j].mv.n ||
+ c_rp[i * 128 + j].ref != a_rp[i * 128 + j].ref)
+ {
+ if (fail()) {
+ fprintf(stderr, "[%d][%d] c_rp.mv.x = 0x%x a_rp.mv.x = 0x%x\n",
+ i, j, c_rp[i * 128 + j].mv.x, a_rp[i * 128 + j].mv.x);
+ fprintf(stderr, "[%d][%d] c_rp.mv.y = 0x%x a_rp.mv.y = 0x%x\n",
+ i, j, c_rp[i * 128 + j].mv.y, a_rp[i * 128 + j].mv.y);
+ fprintf(stderr, "[%d][%d] c_rp.ref = %u a_rp.ref = %u\n",
+ i, j, c_rp[i * 128 + j].ref, a_rp[i * 128 + j].ref);
+ }
+ }
+
+ for (int bs = BS_4x4; bs < N_BS_SIZES; bs++) {
+ const int bw8 = (dav1d_block_dimensions[bs][0] + 1) >> 1;
+ for (int i = 0; i < 16; i++)
+ for (int j = 0; j < 128; j += bw8) {
+ rr[i * 2][j * 2 + 1].ref.ref[0] = (rnd() % 9) - 1;
+ rr[i * 2][j * 2 + 1].ref.ref[1] = (rnd() % 9) - 1;
+ rr[i * 2][j * 2 + 1].bs = bs;
+ }
+ bench_new(alternate(c_rp, a_rp), 128, rr, ref_sign, 128, 16, 0, 0);
+ }
+ }
+
+ report("save_tmvs");
+}
+
+static void check_splat_mv(const Dav1dRefmvsDSPContext *const c) {
+ ALIGN_STK_64(refmvs_block, c_buf, 32 * 32,);
+ ALIGN_STK_64(refmvs_block, a_buf, 32 * 32,);
+ refmvs_block *c_dst[32];
+ refmvs_block *a_dst[32];
+ const size_t stride = 32 * sizeof(refmvs_block);
+
+ for (int i = 0; i < 32; i++) {
+ c_dst[i] = c_buf + 32 * i;
+ a_dst[i] = a_buf + 32 * i;
+ }
+
+ declare_func(void, refmvs_block **rr, const refmvs_block *rmv,
+ int bx4, int bw4, int bh4);
+
+ for (int w = 1; w <= 32; w *= 2) {
+ if (check_func(c->splat_mv, "splat_mv_w%d", w)) {
+ const int h_min = imax(w / 4, 1);
+ const int h_max = imin(w * 4, 32);
+ const int w_uint32 = w * sizeof(refmvs_block) / sizeof(uint32_t);
+ for (int h = h_min; h <= h_max; h *= 2) {
+ const int offset = (int) ((unsigned) w * rnd()) & 31;
+ union {
+ refmvs_block rmv;
+ uint32_t u32[3];
+ } ALIGN(tmp, 16);
+ tmp.u32[0] = rnd();
+ tmp.u32[1] = rnd();
+ tmp.u32[2] = rnd();
+
+ call_ref(c_dst, &tmp.rmv, offset, w, h);
+ call_new(a_dst, &tmp.rmv, offset, w, h);
+ checkasm_check(uint32_t, (uint32_t*)(c_buf + offset), stride,
+ (uint32_t*)(a_buf + offset), stride,
+ w_uint32, h, "dst");
+
+ bench_new(a_dst, &tmp.rmv, 0, w, h);
+ }
+ }
+ }
+ report("splat_mv");
+}
+
+void checkasm_check_refmvs(void) {
+ Dav1dRefmvsDSPContext c;
+ dav1d_refmvs_dsp_init(&c);
+
+ check_save_tmvs(&c);
+ check_splat_mv(&c);
+}
diff --git a/third_party/dav1d/tests/checkasm/x86/checkasm.asm b/third_party/dav1d/tests/checkasm/x86/checkasm.asm
new file mode 100644
index 0000000000..8f19ef97f7
--- /dev/null
+++ b/third_party/dav1d/tests/checkasm/x86/checkasm.asm
@@ -0,0 +1,475 @@
+; Copyright © 2018, VideoLAN and dav1d authors
+; Copyright © 2018, Two Orioles, LLC
+; All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions are met:
+;
+; 1. Redistributions of source code must retain the above copyright notice, this
+; list of conditions and the following disclaimer.
+;
+; 2. Redistributions in binary form must reproduce the above copyright notice,
+; this list of conditions and the following disclaimer in the documentation
+; and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+; ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+%include "config.asm"
+%undef private_prefix
+%define private_prefix checkasm
+%include "ext/x86/x86inc.asm"
+
+SECTION_RODATA 16
+
+%if ARCH_X86_64
+; just random numbers to reduce the chance of incidental match
+%if WIN64
+x6: dq 0x1a1b2550a612b48c,0x79445c159ce79064
+x7: dq 0x2eed899d5a28ddcd,0x86b2536fcd8cf636
+x8: dq 0xb0856806085e7943,0x3f2bf84fc0fcca4e
+x9: dq 0xacbd382dcf5b8de2,0xd229e1f5b281303f
+x10: dq 0x71aeaff20b095fd9,0xab63e2e11fa38ed9
+x11: dq 0x89b0c0765892729a,0x77d410d5c42c882d
+x12: dq 0xc45ea11a955d8dd5,0x24b3c1d2a024048b
+x13: dq 0x2e8ec680de14b47c,0xdd7b8919edd42786
+x14: dq 0x135ce6888fa02cbf,0x11e53e2b2ac655ef
+x15: dq 0x011ff554472a7a10,0x6de8f4c914c334d5
+n7: dq 0x21f86d66c8ca00ce
+n8: dq 0x75b6ba21077c48ad
+%endif
+n9: dq 0xed56bb2dcb3c7736
+n10: dq 0x8bda43d3fd1a7e06
+n11: dq 0xb64a9c9e5d318408
+n12: dq 0xdf9a54b303f1d3a3
+n13: dq 0x4a75479abd64e097
+n14: dq 0x249214109d5d1c88
+%endif
+
+errmsg_stack: db "stack corruption", 0
+errmsg_register: db "failed to preserve register:%s", 0
+errmsg_vzeroupper: db "missing vzeroupper", 0
+
+SECTION .bss
+
+check_vzeroupper: resd 1
+
+SECTION .text
+
+cextern fail_func
+
+; max number of args used by any asm function.
+; (max_args % 4) must equal 3 for stack alignment
+%define max_args 15
+
+%if UNIX64
+ DECLARE_REG_TMP 0
+%else
+ DECLARE_REG_TMP 4
+%endif
+
+;-----------------------------------------------------------------------------
+; unsigned checkasm_init_x86(char *name)
+;-----------------------------------------------------------------------------
+cglobal init_x86, 0, 5
+%if ARCH_X86_64
+ push rbx
+%endif
+ movifnidn t0, r0mp
+ mov eax, 0x80000000
+ cpuid
+ cmp eax, 0x80000004
+ jb .no_brand ; processor brand string not supported
+ mov eax, 0x80000002
+ cpuid
+ mov [t0+4* 0], eax
+ mov [t0+4* 1], ebx
+ mov [t0+4* 2], ecx
+ mov [t0+4* 3], edx
+ mov eax, 0x80000003
+ cpuid
+ mov [t0+4* 4], eax
+ mov [t0+4* 5], ebx
+ mov [t0+4* 6], ecx
+ mov [t0+4* 7], edx
+ mov eax, 0x80000004
+ cpuid
+ mov [t0+4* 8], eax
+ mov [t0+4* 9], ebx
+ mov [t0+4*10], ecx
+ mov [t0+4*11], edx
+ xor eax, eax
+ cpuid
+ jmp .check_xcr1
+.no_brand: ; use manufacturer id as a fallback
+ xor eax, eax
+ mov [t0+4*3], eax
+ cpuid
+ mov [t0+4*0], ebx
+ mov [t0+4*1], edx
+ mov [t0+4*2], ecx
+.check_xcr1:
+ test eax, eax
+ jz .end2 ; cpuid leaf 1 not supported
+ mov t0d, eax ; max leaf
+ mov eax, 1
+ cpuid
+ and ecx, 0x18000000
+ cmp ecx, 0x18000000
+ jne .end2 ; osxsave/avx not supported
+ cmp t0d, 13 ; cpuid leaf 13 not supported
+ jb .end2
+ mov t0d, eax ; cpuid signature
+ mov eax, 13
+ mov ecx, 1
+ cpuid
+ test al, 0x04
+ jz .end ; xcr1 not supported
+ mov ecx, 1
+ xgetbv
+ test al, 0x04
+ jnz .end ; always-dirty ymm state
+%if ARCH_X86_64 == 0 && PIC
+ LEA eax, check_vzeroupper
+ mov [eax], ecx
+%else
+ mov [check_vzeroupper], ecx
+%endif
+.end:
+ mov eax, t0d
+.end2:
+%if ARCH_X86_64
+ pop rbx
+%endif
+ RET
+
+%if ARCH_X86_64
+%if WIN64
+ %define stack_param rsp+32 ; shadow space
+ %define num_fn_args rsp+stack_offset+17*8
+ %assign num_reg_args 4
+ %assign free_regs 7
+ %assign clobber_mask_stack_bit 16
+ DECLARE_REG_TMP 4
+%else
+ %define stack_param rsp
+ %define num_fn_args rsp+stack_offset+11*8
+ %assign num_reg_args 6
+ %assign free_regs 9
+ %assign clobber_mask_stack_bit 64
+ DECLARE_REG_TMP 7
+%endif
+
+%macro CLOBBER_UPPER 2 ; reg, mask_bit
+ mov r13d, %1d
+ or r13, r8
+ test r9b, %2
+ cmovnz %1, r13
+%endmacro
+
+cglobal checked_call, 2, 15, 16, max_args*8+64+8
+ mov r10d, [num_fn_args]
+ mov r8, 0xdeadbeef00000000
+ mov r9d, [num_fn_args+r10*8+8] ; clobber_mask
+ mov t0, [num_fn_args+r10*8] ; func
+
+ ; Clobber the upper halves of 32-bit parameters
+ CLOBBER_UPPER r0, 1
+ CLOBBER_UPPER r1, 2
+ CLOBBER_UPPER r2, 4
+ CLOBBER_UPPER r3, 8
+%if UNIX64
+ CLOBBER_UPPER r4, 16
+ CLOBBER_UPPER r5, 32
+%else ; WIN64
+%assign i 6
+%rep 16-6
+ mova m %+ i, [x %+ i]
+ %assign i i+1
+%endrep
+%endif
+
+ xor r11d, r11d
+ sub r10d, num_reg_args
+ cmovs r10d, r11d ; num stack args
+
+ ; write stack canaries to the area above parameters passed on the stack
+ mov r12, [rsp+stack_offset] ; return address
+ not r12
+%assign i 0
+%rep 8 ; 64 bytes
+ mov [stack_param+(r10+i)*8], r12
+ %assign i i+1
+%endrep
+
+ test r10d, r10d
+ jz .stack_setup_done ; no stack parameters
+.copy_stack_parameter:
+ mov r12, [stack_param+stack_offset+8+r11*8]
+ CLOBBER_UPPER r12, clobber_mask_stack_bit
+ shr r9d, 1
+ mov [stack_param+r11*8], r12
+ inc r11d
+ cmp r11d, r10d
+ jl .copy_stack_parameter
+.stack_setup_done:
+
+%assign i 14
+%rep 15-free_regs
+ mov r %+ i, [n %+ i]
+ %assign i i-1
+%endrep
+ call t0
+
+ ; check for stack corruption
+ mov r0d, [num_fn_args]
+ xor r3d, r3d
+ sub r0d, num_reg_args
+ cmovs r0d, r3d ; num stack args
+
+ mov r3, [rsp+stack_offset]
+ mov r4, [stack_param+r0*8]
+ not r3
+ xor r4, r3
+%assign i 1
+%rep 6
+ mov r5, [stack_param+(r0+i)*8]
+ xor r5, r3
+ or r4, r5
+ %assign i i+1
+%endrep
+ xor r3, [stack_param+(r0+7)*8]
+ or r4, r3
+ jz .stack_ok
+ ; Save the return value located in rdx:rax first to prevent clobbering.
+ mov r10, rax
+ mov r11, rdx
+ lea r0, [errmsg_stack]
+ jmp .fail
+.stack_ok:
+
+ ; check for failure to preserve registers
+%assign i 14
+%rep 15-free_regs
+ cmp r %+ i, [n %+ i]
+ setne r4b
+ lea r3d, [r4+r3*2]
+ %assign i i-1
+%endrep
+%if WIN64
+ lea r0, [rsp+32] ; account for shadow space
+ mov r5, r0
+ test r3d, r3d
+ jz .gpr_ok
+%else
+ test r3d, r3d
+ jz .gpr_xmm_ok
+ mov r0, rsp
+%endif
+%assign i free_regs
+%rep 15-free_regs
+%if i < 10
+ mov dword [r0], " r0" + (i << 16)
+ lea r4, [r0+3]
+%else
+ mov dword [r0], " r10" + ((i - 10) << 24)
+ lea r4, [r0+4]
+%endif
+ test r3b, 1 << (i - free_regs)
+ cmovnz r0, r4
+ %assign i i+1
+%endrep
+%if WIN64 ; xmm registers
+.gpr_ok:
+%assign i 6
+%rep 16-6
+ pxor m %+ i, [x %+ i]
+ %assign i i+1
+%endrep
+ packsswb m6, m7
+ packsswb m8, m9
+ packsswb m10, m11
+ packsswb m12, m13
+ packsswb m14, m15
+ packsswb m6, m6
+ packsswb m8, m10
+ packsswb m12, m14
+ packsswb m6, m6
+ packsswb m8, m12
+ packsswb m6, m8
+ pxor m7, m7
+ pcmpeqb m6, m7
+ pmovmskb r3d, m6
+ cmp r3d, 0xffff
+ je .xmm_ok
+ mov r7d, " xmm"
+%assign i 6
+%rep 16-6
+ mov [r0+0], r7d
+%if i < 10
+ mov byte [r0+4], "0" + i
+ lea r4, [r0+5]
+%else
+ mov word [r0+4], "10" + ((i - 10) << 8)
+ lea r4, [r0+6]
+%endif
+ test r3d, 1 << i
+ cmovz r0, r4
+ %assign i i+1
+%endrep
+.xmm_ok:
+ cmp r0, r5
+ je .gpr_xmm_ok
+ mov byte [r0], 0
+ mov r11, rdx
+ mov r1, r5
+%else
+ mov byte [r0], 0
+ mov r11, rdx
+ mov r1, rsp
+%endif
+ mov r10, rax
+ lea r0, [errmsg_register]
+ jmp .fail
+.gpr_xmm_ok:
+ ; Check for dirty YMM state, i.e. missing vzeroupper
+ mov ecx, [check_vzeroupper]
+ test ecx, ecx
+ jz .ok ; not supported, skip
+ mov r10, rax
+ mov r11, rdx
+ xgetbv
+ test al, 0x04
+ jz .restore_retval ; clean ymm state
+ lea r0, [errmsg_vzeroupper]
+ vzeroupper
+.fail:
+ ; Call fail_func() with a descriptive message to mark it as a failure.
+ xor eax, eax
+ call fail_func
+.restore_retval:
+ mov rax, r10
+ mov rdx, r11
+.ok:
+ RET
+
+; trigger a warmup of vector units
+%macro WARMUP 0
+cglobal warmup, 0, 0
+ xorps m0, m0
+ mulps m0, m0
+ RET
+%endmacro
+
+INIT_YMM avx2
+WARMUP
+INIT_ZMM avx512
+WARMUP
+
+%else
+
+; just random numbers to reduce the chance of incidental match
+%assign n3 0x6549315c
+%assign n4 0xe02f3e23
+%assign n5 0xb78d0d1d
+%assign n6 0x33627ba7
+
+;-----------------------------------------------------------------------------
+; void checkasm_checked_call(void *func, ...)
+;-----------------------------------------------------------------------------
+cglobal checked_call, 1, 7
+ mov r3, [esp+stack_offset] ; return address
+ mov r1, [esp+stack_offset+17*4] ; num_stack_params
+ mov r2, 27
+ not r3
+ sub r2, r1
+.push_canary:
+ push r3
+ dec r2
+ jg .push_canary
+.push_parameter:
+ push dword [esp+32*4]
+ dec r1
+ jg .push_parameter
+ mov r3, n3
+ mov r4, n4
+ mov r5, n5
+ mov r6, n6
+ call r0
+
+ ; check for failure to preserve registers
+ cmp r3, n3
+ setne r3h
+ cmp r4, n4
+ setne r3b
+ shl r3d, 16
+ cmp r5, n5
+ setne r3h
+ cmp r6, n6
+ setne r3b
+ test r3, r3
+ jz .gpr_ok
+ lea r1, [esp+16]
+ mov [esp+4], r1
+%assign i 3
+%rep 4
+ mov dword [r1], " r0" + (i << 16)
+ lea r4, [r1+3]
+ test r3, 1 << ((6 - i) * 8)
+ cmovnz r1, r4
+ %assign i i+1
+%endrep
+ mov byte [r1], 0
+ mov r5, eax
+ mov r6, edx
+ LEA r1, errmsg_register
+ jmp .fail
+.gpr_ok:
+ ; check for stack corruption
+ mov r3, [esp+48*4] ; num_stack_params
+ mov r6, [esp+31*4] ; return address
+ mov r4, [esp+r3*4]
+ sub r3, 26
+ not r6
+ xor r4, r6
+.check_canary:
+ mov r5, [esp+(r3+27)*4]
+ xor r5, r6
+ or r4, r5
+ inc r3
+ jl .check_canary
+ mov r5, eax
+ mov r6, edx
+ test r4, r4
+ jz .stack_ok
+ LEA r1, errmsg_stack
+ jmp .fail
+.stack_ok:
+ ; check for dirty YMM state, i.e. missing vzeroupper
+ LEA ecx, check_vzeroupper
+ mov ecx, [ecx]
+ test ecx, ecx
+ jz .ok ; not supported, skip
+ xgetbv
+ test al, 0x04
+ jz .ok ; clean ymm state
+ LEA r1, errmsg_vzeroupper
+ vzeroupper
+.fail:
+ mov [esp], r1
+ call fail_func
+.ok:
+ add esp, 27*4
+ mov eax, r5
+ mov edx, r6
+ RET
+
+%endif ; ARCH_X86_64
diff --git a/third_party/dav1d/tests/dav1d_argon.bash b/third_party/dav1d/tests/dav1d_argon.bash
new file mode 100755
index 0000000000..0c35663834
--- /dev/null
+++ b/third_party/dav1d/tests/dav1d_argon.bash
@@ -0,0 +1,175 @@
+#!/usr/bin/env bash
+
+DAV1D="tools/dav1d"
+ARGON_DIR='.'
+FILMGRAIN=1
+CPUMASK=-1
+THREADS=0
+JOBS=1
+
+usage() {
+ NAME=$(basename "$0")
+ {
+ printf "Usage: %s [-d dav1d] [-a argondir] [-g \$filmgrain] [-c \$cpumask] [-t threads] [-j jobs] [DIRECTORY]...\n" "$NAME"
+ printf "Example: %s -d /path/to/dav1d -a /path/to/argon/ -g 0 -c avx2 profile0_core\n" "$NAME"
+ printf "Used to verify that dav1d can decode the Argon AV1 test vectors correctly.\n\n"
+ printf " DIRECTORY one or more dirs in the argon folder to check against\n"
+ printf " (default: everything except large scale tiles and stress files)\n"
+ printf " -d dav1d path to dav1d executable (default: tools/dav1d)\n"
+ printf " -a dir path to argon dir (default: 'tests/argon' if found; '.' otherwise)\n"
+ printf " -g \$num enable filmgrain (default: 1)\n"
+ printf " -c \$mask use restricted cpumask (default: -1)\n"
+ printf " -t \$num number of threads per dav1d (default: 0)\n"
+ printf " -j \$num number of parallel dav1d processes (default: 1)\n\n"
+ } >&2
+ exit 1
+}
+
+error() {
+ printf "\033[1;91m%s\033[0m\n" "$*" >&2
+ exit 1
+}
+
+fail() {
+ printf "\033[1K\rMismatch in %s\n" "$1"
+ (( failed++ ))
+}
+
+check_pids() {
+ new_pids=()
+ done_pids=()
+ for p in "${pids[@]}"; do
+ if kill -0 "$p" 2>/dev/null; then
+ new_pids+=("$p")
+ else
+ done_pids+=("$p")
+ fi
+ done
+ pids=("${new_pids[@]}")
+}
+
+wait_pids() {
+ pid_list=("$@")
+ for p in "${pid_list[@]}"; do
+ if ! wait "$p"; then
+ local file_varname="file$p"
+ fail "${!file_varname}"
+ fi
+ done
+}
+
+block_pids() {
+ while [ ${#pids[@]} -ge "$JOBS" ]; do
+ check_pids
+ if [ ${#done_pids} -eq 0 ]; then
+ sleep 0.2
+ else
+ wait_pids "${done_pids[@]}"
+ fi
+ done
+}
+
+wait_all_pids() {
+ wait_pids "${pids[@]}"
+}
+
+# find tests/argon
+tests_dir=$(dirname "$(readlink -f "$0")")
+if [ -d "$tests_dir/argon" ]; then
+ ARGON_DIR="$tests_dir/argon"
+fi
+
+while getopts ":d:a:g:c:t:j:" opt; do
+ case "$opt" in
+ d)
+ DAV1D="$OPTARG"
+ ;;
+ a)
+ ARGON_DIR="$OPTARG"
+ ;;
+ g)
+ FILMGRAIN="$OPTARG"
+ ;;
+ c)
+ CPUMASK="$OPTARG"
+ ;;
+ t)
+ THREADS="$OPTARG"
+ ;;
+ j)
+ JOBS="$OPTARG"
+ ;;
+ \?)
+ printf "Error! Invalid option: -%s\n" "$OPTARG" >&2
+ usage
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [ "$#" -eq 0 ]; then
+ # Everything except large scale tiles and stress files.
+ dirs=("$ARGON_DIR/profile0_core" "$ARGON_DIR/profile0_core_special"
+ "$ARGON_DIR/profile0_not_annexb" "$ARGON_DIR/profile0_not_annexb_special"
+ "$ARGON_DIR/profile1_core" "$ARGON_DIR/profile1_core_special"
+ "$ARGON_DIR/profile1_not_annexb" "$ARGON_DIR/profile1_not_annexb_special"
+ "$ARGON_DIR/profile2_core" "$ARGON_DIR/profile2_core_special"
+ "$ARGON_DIR/profile2_not_annexb" "$ARGON_DIR/profile2_not_annexb_special"
+ "$ARGON_DIR/profile_switching")
+else
+ mapfile -t dirs < <(printf "${ARGON_DIR}/%s\n" "$@" | sort -u)
+fi
+
+ver_info="dav1d $("$DAV1D" -v 2>&1) filmgrain=$FILMGRAIN cpumask=$CPUMASK" || error "Error! Can't run $DAV1D"
+files=()
+
+for d in "${dirs[@]}"; do
+ if [ -d "$d/streams" ]; then
+ files+=("${d/%\//}"/streams/*.obu)
+ fi
+done
+
+if [ ${#files[@]} -eq 0 ]; then
+ error "Error! No files found at ${dirs[*]}"
+fi
+
+failed=0
+pids=()
+for i in "${!files[@]}"; do
+ f="${files[i]}"
+ if [ "$FILMGRAIN" -eq 0 ]; then
+ md5=${f/\/streams\//\/md5_no_film_grain\/}
+ else
+ md5=${f/\/streams\//\/md5_ref\/}
+ fi
+ md5=$(<"${md5/%obu/md5}") || error "Error! Can't read md5 ${md5} for file ${f}"
+ md5=${md5/ */}
+
+ printf "\033[1K\r[%3d%% %d/%d] Verifying %s" "$(((i+1)*100/${#files[@]}))" "$((i+1))" "${#files[@]}" "$f"
+ cmd=("$DAV1D" -i "$f" --filmgrain "$FILMGRAIN" --verify "$md5" --cpumask "$CPUMASK" --threads "$THREADS" -q)
+ if [ "$JOBS" -gt 1 ]; then
+ "${cmd[@]}" 2>/dev/null &
+ p=$!
+ pids+=("$p")
+ declare "file$p=$f"
+ block_pids
+ else
+ if ! "${cmd[@]}" 2>/dev/null; then
+ fail "$f"
+ fi
+ fi
+done
+
+wait_all_pids
+
+if [ "$failed" -ne 0 ]; then
+ printf "\033[1K\r%d/%d files \033[1;91mfailed\033[0m to verify" "$failed" "${#files[@]}"
+else
+ printf "\033[1K\r%d files \033[1;92msuccessfully\033[0m verified" "${#files[@]}"
+fi
+printf " in %dm%ds (%s)\n" "$((SECONDS/60))" "$((SECONDS%60))" "$ver_info"
+
+exit $failed
diff --git a/third_party/dav1d/tests/header_test.c b/third_party/dav1d/tests/header_test.c
new file mode 100644
index 0000000000..2cd9eca7f9
--- /dev/null
+++ b/third_party/dav1d/tests/header_test.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include DAV1D_TEST_HEADER
+
+int main(void)
+{
+ return 0;
+}
diff --git a/third_party/dav1d/tests/libfuzzer/alloc_fail.c b/third_party/dav1d/tests/libfuzzer/alloc_fail.c
new file mode 100644
index 0000000000..ddd1dd71ab
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/alloc_fail.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "alloc_fail.h"
+
+static int fail_probability;
+
+void dav1d_setup_alloc_fail(unsigned seed, unsigned probability) {
+ srand(seed);
+
+ while (probability >= RAND_MAX)
+ probability >>= 1;
+
+ fail_probability = probability;
+}
+
+void * __wrap_malloc(size_t);
+
+void * __wrap_malloc(size_t sz) {
+ if (rand() < fail_probability)
+ return NULL;
+ return malloc(sz);
+}
+
+#if defined(HAVE_POSIX_MEMALIGN)
+int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size);
+
+int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size) {
+ if (rand() < fail_probability)
+ return ENOMEM;
+ return posix_memalign(memptr, alignment, size);
+}
+#else
+#error "HAVE_POSIX_MEMALIGN required"
+#endif
+
+int __wrap_pthread_create(pthread_t *, const pthread_attr_t *,
+ void *(*) (void *), void *);
+
+int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ if (rand() < (fail_probability + RAND_MAX/16))
+ return EAGAIN;
+
+ return pthread_create(thread, attr, start_routine, arg);
+}
+
+int __wrap_pthread_mutex_init(pthread_mutex_t *,
+ const pthread_mutexattr_t *);
+
+int __wrap_pthread_mutex_init(pthread_mutex_t *restrict mutex,
+ const pthread_mutexattr_t *restrict attr)
+{
+ if (rand() < (fail_probability + RAND_MAX/8))
+ return ENOMEM;
+
+ return pthread_mutex_init(mutex, attr);
+}
+
+int __wrap_pthread_cond_init(pthread_cond_t *,
+ const pthread_condattr_t *);
+
+int __wrap_pthread_cond_init(pthread_cond_t *restrict cond,
+ const pthread_condattr_t *restrict attr)
+{
+ if (rand() < (fail_probability + RAND_MAX/16))
+ return ENOMEM;
+
+ return pthread_cond_init(cond, attr);
+}
diff --git a/third_party/dav1d/tests/libfuzzer/alloc_fail.h b/third_party/dav1d/tests/libfuzzer/alloc_fail.h
new file mode 100644
index 0000000000..5ace870beb
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/alloc_fail.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H
+#define DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H
+
+#include <dav1d/common.h>
+
+DAV1D_API void dav1d_setup_alloc_fail(unsigned seed, unsigned probability);
+
+#endif /* DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H */
diff --git a/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c
new file mode 100644
index 0000000000..c894636f1e
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dav1d/dav1d.h>
+#include "src/cpu.h"
+#include "dav1d_fuzzer.h"
+
+#ifdef DAV1D_ALLOC_FAIL
+
+#include "alloc_fail.h"
+
+static unsigned djb_xor(const uint8_t * c, size_t len) {
+ unsigned hash = 5381;
+ for(size_t i = 0; i < len; i++)
+ hash = hash * 33 ^ c[i];
+ return hash;
+}
+#endif
+
+static unsigned r32le(const uint8_t *const p) {
+ return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0];
+}
+
+#define DAV1D_FUZZ_MAX_SIZE 4096 * 4096
+
+// search for "--cpumask xxx" in argv and remove both parameters
+int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ int i = 1;
+ for (; i < *argc; i++) {
+ if (!strcmp((*argv)[i], "--cpumask")) {
+ const char * cpumask = (*argv)[i+1];
+ if (cpumask) {
+ char *end;
+ unsigned res;
+ if (!strncmp(cpumask, "0x", 2)) {
+ cpumask += 2;
+ res = (unsigned) strtoul(cpumask, &end, 16);
+ } else {
+ res = (unsigned) strtoul(cpumask, &end, 0);
+ }
+ if (end != cpumask && !end[0]) {
+ dav1d_set_cpu_flags_mask(res);
+ }
+ }
+ break;
+ }
+ }
+
+ for (; i < *argc - 2; i++) {
+ (*argv)[i] = (*argv)[i + 2];
+ }
+
+ *argc = i;
+
+ return 0;
+}
+
+
+// expects ivf input
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ Dav1dSettings settings = { 0 };
+ Dav1dContext * ctx = NULL;
+ Dav1dPicture pic;
+ const uint8_t *ptr = data;
+ int have_seq_hdr = 0;
+ int err;
+
+ dav1d_version();
+
+ if (size < 32) goto end;
+#ifdef DAV1D_ALLOC_FAIL
+ unsigned h = djb_xor(ptr, 32);
+ unsigned seed = h;
+ unsigned probability = h > (RAND_MAX >> 5) ? RAND_MAX >> 5 : h;
+ int max_frame_delay = (h & 0xf) + 1;
+ int n_threads = ((h >> 4) & 0x7) + 1;
+ if (max_frame_delay > 5) max_frame_delay = 1;
+ if (n_threads > 3) n_threads = 1;
+#endif
+ ptr += 32; // skip ivf header
+
+ dav1d_default_settings(&settings);
+
+#ifdef DAV1D_MT_FUZZING
+ settings.max_frame_delay = settings.n_threads = 4;
+#elif defined(DAV1D_ALLOC_FAIL)
+ settings.max_frame_delay = max_frame_delay;
+ settings.n_threads = n_threads;
+ dav1d_setup_alloc_fail(seed, probability);
+#else
+ settings.max_frame_delay = settings.n_threads = 1;
+#endif
+#if defined(DAV1D_FUZZ_MAX_SIZE)
+ settings.frame_size_limit = DAV1D_FUZZ_MAX_SIZE;
+#endif
+
+ err = dav1d_open(&ctx, &settings);
+ if (err < 0) goto end;
+
+ while (ptr <= data + size - 12) {
+ Dav1dData buf;
+ uint8_t *p;
+
+ size_t frame_size = r32le(ptr);
+ ptr += 12;
+
+ if (frame_size > size || ptr > data + size - frame_size)
+ break;
+
+ if (!frame_size) continue;
+
+ if (!have_seq_hdr) {
+ Dav1dSequenceHeader seq = { 0 };
+ int err = dav1d_parse_sequence_header(&seq, ptr, frame_size);
+ // skip frames until we see a sequence header
+ if (err != 0) {
+ ptr += frame_size;
+ continue;
+ }
+ have_seq_hdr = 1;
+ }
+
+ // copy frame data to a new buffer to catch reads past the end of input
+ p = dav1d_data_create(&buf, frame_size);
+ if (!p) goto cleanup;
+ memcpy(p, ptr, frame_size);
+ ptr += frame_size;
+
+ do {
+ if ((err = dav1d_send_data(ctx, &buf)) < 0) {
+ if (err != DAV1D_ERR(EAGAIN))
+ break;
+ }
+ memset(&pic, 0, sizeof(pic));
+ err = dav1d_get_picture(ctx, &pic);
+ if (err == 0) {
+ dav1d_picture_unref(&pic);
+ } else if (err != DAV1D_ERR(EAGAIN)) {
+ break;
+ }
+ } while (buf.sz > 0);
+
+ if (buf.sz > 0)
+ dav1d_data_unref(&buf);
+ }
+
+ memset(&pic, 0, sizeof(pic));
+ if ((err = dav1d_get_picture(ctx, &pic)) == 0) {
+ /* Test calling dav1d_picture_unref() after dav1d_close() */
+ do {
+ Dav1dPicture pic2 = { 0 };
+ if ((err = dav1d_get_picture(ctx, &pic2)) == 0)
+ dav1d_picture_unref(&pic2);
+ } while (err != DAV1D_ERR(EAGAIN));
+
+ dav1d_close(&ctx);
+ dav1d_picture_unref(&pic);
+ return 0;
+ }
+
+cleanup:
+ dav1d_close(&ctx);
+end:
+ return 0;
+}
diff --git a/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h
new file mode 100644
index 0000000000..0cbbad46b0
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H
+#define DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+int LLVMFuzzerInitialize(int *argc, char ***argv);
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+#endif /* DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H */
diff --git a/third_party/dav1d/tests/libfuzzer/main.c b/third_party/dav1d/tests/libfuzzer/main.c
new file mode 100644
index 0000000000..8647738666
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/main.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Janne Grunau
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "dav1d_fuzzer.h"
+
+// expects ivf input
+
+int main(int argc, char *argv[]) {
+ int ret = -1;
+ FILE *f = NULL;
+ int64_t fsize;
+ const char *filename = NULL;
+ uint8_t *data = NULL;
+ size_t size = 0;
+
+ if (LLVMFuzzerInitialize(&argc, &argv)) {
+ return 1;
+ }
+
+ if (argc != 2) {
+ fprintf(stdout, "Usage:\n%s fuzzing_testcase.ivf\n", argv[0]);
+ return -1;
+ }
+ filename = argv[1];
+
+ if (!(f = fopen(filename, "rb"))) {
+ fprintf(stderr, "failed to open %s: %s\n", filename, strerror(errno));
+ goto error;
+ }
+
+ if (fseeko(f, 0, SEEK_END) == -1) {
+ fprintf(stderr, "fseek(%s, 0, SEEK_END) failed: %s\n", filename,
+ strerror(errno));
+ goto error;
+ }
+ if ((fsize = ftello(f)) == -1) {
+ fprintf(stderr, "ftell(%s) failed: %s\n", filename, strerror(errno));
+ goto error;
+ }
+ rewind(f);
+
+ if (fsize < 0 || fsize > INT_MAX) {
+ fprintf(stderr, "%s is too large: %"PRId64"\n", filename, fsize);
+ goto error;
+ }
+ size = (size_t)fsize;
+
+ if (!(data = malloc(size))) {
+ fprintf(stderr, "failed to allocate: %zu bytes\n", size);
+ goto error;
+ }
+
+ if (fread(data, size, 1, f) == size) {
+ fprintf(stderr, "failed to read %zu bytes from %s: %s\n", size,
+ filename, strerror(errno));
+ goto error;
+ }
+
+ ret = LLVMFuzzerTestOneInput(data, size);
+
+error:
+ free(data);
+ if (f) fclose(f);
+ return ret;
+}
diff --git a/third_party/dav1d/tests/libfuzzer/meson.build b/third_party/dav1d/tests/libfuzzer/meson.build
new file mode 100644
index 0000000000..45d28562c1
--- /dev/null
+++ b/third_party/dav1d/tests/libfuzzer/meson.build
@@ -0,0 +1,101 @@
+# Copyright © 2020, VideoLAN and dav1d authors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# Build definition for the dav1d fuzzing binaries
+#
+
+if fuzzing_engine == 'none' and not have_fseeko
+ subdir_done()
+endif
+
+dav1d_fuzzer_sources = files('dav1d_fuzzer.c')
+fuzzer_ldflags = []
+fuzzer_link_lang = {}
+
+if get_option('fuzzer_ldflags') != ''
+ fuzzer_ldflags += [get_option('fuzzer_ldflags')]
+endif
+
+if fuzzing_engine == 'none'
+ dav1d_fuzzer_sources += files('main.c')
+elif fuzzing_engine == 'libfuzzer'
+ fuzzer_ldflags += ['-fsanitize=fuzzer']
+elif fuzzing_engine == 'oss-fuzz'
+ # libFuzzingEngine needs c++
+ add_languages('cpp')
+ fuzzer_link_lang = {'link_language': 'cpp'}
+endif
+
+dav1d_fuzzer = executable('dav1d_fuzzer',
+ dav1d_fuzzer_sources,
+ include_directories: dav1d_inc_dirs,
+ link_args: fuzzer_ldflags,
+ link_with : libdav1d,
+ build_by_default: true,
+ dependencies : [thread_dependency],
+ kwargs: fuzzer_link_lang
+ )
+
+dav1d_fuzzer_mt = executable('dav1d_fuzzer_mt',
+ dav1d_fuzzer_sources,
+ include_directories: dav1d_inc_dirs,
+ c_args: ['-DDAV1D_MT_FUZZING'],
+ link_args: fuzzer_ldflags,
+ link_with : libdav1d,
+ build_by_default: true,
+ dependencies : [thread_dependency],
+ kwargs: fuzzer_link_lang
+ )
+
+objcopy = find_program('objcopy',
+ required: false)
+if (objcopy.found() and
+ not get_option('b_lto') and
+ get_option('default_library') == 'static' and
+ cc.has_function('posix_memalign', prefix : '#include <stdlib.h>', args : test_args))
+
+ libdav1d_af = custom_target('libdav1d_af',
+ input: libdav1d,
+ output: 'libdav1d_af.a',
+ depends: libdav1d,
+ command: [objcopy,
+ '--redefine-sym', 'malloc=__wrap_malloc',
+ '--redefine-sym', 'posix_memalign=__wrap_posix_memalign',
+ '--redefine-sym', 'pthread_create=__wrap_pthread_create',
+ '--redefine-sym', 'pthread_cond_init=__wrap_pthread_cond_init',
+ '--redefine-sym', 'pthread_mutex_init=__wrap_pthread_mutex_init',
+ '@INPUT@', '@OUTPUT@'])
+
+ dav1d_fuzzer_mem = executable('dav1d_fuzzer_mem',
+ dav1d_fuzzer_sources + ['alloc_fail.c'],
+ include_directories: dav1d_inc_dirs,
+ c_args: ['-DDAV1D_ALLOC_FAIL'],
+ link_args: fuzzer_ldflags + [join_paths(libdav1d_af.full_path())],
+ link_depends: libdav1d_af,
+ build_by_default: false,
+ dependencies : [thread_dependency],
+ kwargs: fuzzer_link_lang
+ )
+endif
diff --git a/third_party/dav1d/tests/meson.build b/third_party/dav1d/tests/meson.build
new file mode 100644
index 0000000000..90de417727
--- /dev/null
+++ b/third_party/dav1d/tests/meson.build
@@ -0,0 +1,152 @@
+# Copyright © 2018, VideoLAN and dav1d authors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#
+# Build definition for the dav1d tests
+#
+
+# Leave subdir if tests are disabled
+if not get_option('enable_tests')
+ subdir_done()
+endif
+
+if is_asm_enabled
+ checkasm_sources = files(
+ 'checkasm/checkasm.c',
+ 'checkasm/msac.c',
+ 'checkasm/refmvs.c',
+ )
+
+ checkasm_tmpl_sources = files(
+ 'checkasm/cdef.c',
+ 'checkasm/filmgrain.c',
+ 'checkasm/ipred.c',
+ 'checkasm/itx.c',
+ 'checkasm/loopfilter.c',
+ 'checkasm/looprestoration.c',
+ 'checkasm/mc.c',
+ )
+
+ checkasm_bitdepth_objs = []
+ foreach bitdepth : dav1d_bitdepths
+ checkasm_bitdepth_lib = static_library(
+ 'checkasm_bitdepth_@0@'.format(bitdepth),
+ checkasm_tmpl_sources,
+ include_directories: dav1d_inc_dirs,
+ dependencies : [stdatomic_dependencies],
+ c_args: ['-DBITDEPTH=@0@'.format(bitdepth)],
+ install: false,
+ build_by_default: false,
+ )
+ checkasm_bitdepth_objs += checkasm_bitdepth_lib.extract_all_objects(recursive: true)
+ endforeach
+
+ checkasm_asm_objs = []
+ checkasm_asm_sources = []
+ if host_machine.cpu_family() == 'aarch64' or host_machine.cpu() == 'arm64'
+ checkasm_asm_sources += files('checkasm/arm/checkasm_64.S')
+ elif host_machine.cpu_family().startswith('arm')
+ checkasm_asm_sources += files('checkasm/arm/checkasm_32.S')
+ elif host_machine.cpu_family().startswith('x86')
+ checkasm_asm_objs += nasm_gen.process(files('checkasm/x86/checkasm.asm'))
+ endif
+
+ if use_gaspp
+ checkasm_asm_objs += gaspp_gen.process(checkasm_asm_sources)
+ else
+ checkasm_sources += checkasm_asm_sources
+ endif
+
+ checkasm = executable('checkasm',
+ checkasm_sources,
+ checkasm_asm_objs,
+
+ objects: [
+ checkasm_bitdepth_objs,
+ libdav1d.extract_all_objects(recursive: true),
+ ],
+
+ include_directories: dav1d_inc_dirs,
+ build_by_default: false,
+ dependencies : [
+ thread_dependency,
+ rt_dependency,
+ libdl_dependency,
+ libm_dependency,
+ ],
+ )
+
+ test('checkasm', checkasm, suite: 'checkasm', timeout: 180, is_parallel: false)
+ benchmark('checkasm', checkasm, suite: 'checkasm', timeout: 3600, args: '--bench')
+endif
+
+c99_extension_flag = cc.first_supported_argument(
+ '-Werror=c11-extensions',
+ '-Werror=c99-c11-compat',
+ '-Wc11-extensions',
+ '-Wc99-c11-compat',
+)
+
+# dav1d_api_headers
+foreach header : dav1d_api_headers
+ target = header + '_test'
+
+ header_test_exe = executable(target,
+ 'header_test.c',
+ include_directories: dav1d_inc_dirs,
+ c_args: ['-DDAV1D_TEST_HEADER="@0@"'.format(header), c99_extension_flag],
+ build_by_default: true
+ )
+
+ test(target, header_test_exe, suite: 'headers')
+endforeach
+
+
+# fuzzing binaries
+subdir('libfuzzer')
+
+# seek stress test binary, depends on dav1d cli tool
+if get_option('enable_tools')
+ seek_stress_sources = files('seek_stress.c')
+ seek_stress = executable('seek_stress',
+ seek_stress_sources, rev_target,
+ objects: [
+ dav1d.extract_objects('dav1d_cli_parse.c'),
+ dav1d_input_objs.extract_objects('input/input.c', 'input/ivf.c'),
+ ],
+ include_directories: [dav1d_inc_dirs, include_directories('../tools')],
+ link_with: libdav1d,
+ dependencies: [
+ thread_dependency,
+ rt_dependency,
+ getopt_dependency,
+ libm_dependency,
+ ],
+ )
+endif
+
+# Include dav1d test data repository with additional tests
+if get_option('testdata_tests')
+ subdir('dav1d-test-data')
+endif
diff --git a/third_party/dav1d/tests/seek_stress.c b/third_party/dav1d/tests/seek_stress.c
new file mode 100644
index 0000000000..a85ec86886
--- /dev/null
+++ b/third_party/dav1d/tests/seek_stress.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2020, VideoLAN and dav1d authors
+ * Copyright © 2020, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "vcs_version.h"
+#include "cli_config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dav1d/dav1d.h"
+#include "input/input.h"
+#include "input/demuxer.h"
+#include "dav1d_cli_parse.h"
+
+#define NUM_RAND_SEEK 3
+#define NUM_REL_SEEK 4
+#define NUM_END_SEEK 2
+
+const Demuxer annexb_demuxer = { .name = "" };
+const Demuxer section5_demuxer = { .name = "" };
+
+#ifdef _WIN32
+#include <windows.h>
+static unsigned get_seed(void) {
+ return GetTickCount();
+}
+#else
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#else
+#include <time.h>
+#endif
+static unsigned get_seed(void) {
+#ifdef __APPLE__
+ return (unsigned) mach_absolute_time();
+#elif defined(HAVE_CLOCK_GETTIME)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (unsigned) (1000000000ULL * ts.tv_sec + ts.tv_nsec);
+#endif
+}
+#endif
+
+static uint32_t xs_state[4];
+
+static void xor128_srand(unsigned seed) {
+ xs_state[0] = seed;
+ xs_state[1] = ( seed & 0xffff0000) | (~seed & 0x0000ffff);
+ xs_state[2] = (~seed & 0xffff0000) | ( seed & 0x0000ffff);
+ xs_state[3] = ~seed;
+}
+
+// xor128 from Marsaglia, George (July 2003). "Xorshift RNGs".
+// Journal of Statistical Software. 8 (14).
+// doi:10.18637/jss.v008.i14.
+static int xor128_rand(void) {
+ const uint32_t x = xs_state[0];
+ const uint32_t t = x ^ (x << 11);
+
+ xs_state[0] = xs_state[1];
+ xs_state[1] = xs_state[2];
+ xs_state[2] = xs_state[3];
+ uint32_t w = xs_state[3];
+
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+ xs_state[3] = w;
+
+ return w >> 1;
+}
+
+static inline int decode_frame(Dav1dPicture *const p,
+ Dav1dContext *const c, Dav1dData *const data)
+{
+ int res;
+ memset(p, 0, sizeof(*p));
+ if ((res = dav1d_send_data(c, data)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ fprintf(stderr, "Error decoding frame: %s\n",
+ strerror(DAV1D_ERR(res)));
+ return res;
+ }
+ }
+ if ((res = dav1d_get_picture(c, p)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ fprintf(stderr, "Error decoding frame: %s\n",
+ strerror(DAV1D_ERR(res)));
+ return res;
+ }
+ } else dav1d_picture_unref(p);
+ return 0;
+}
+
+static int decode_rand(DemuxerContext *const in, Dav1dContext *const c,
+ Dav1dData *const data, const double fps)
+{
+ int res = 0;
+ Dav1dPicture p;
+ const int num_frames = xor128_rand() % (int)(fps * 5);
+ for (int i = 0; i < num_frames; i++) {
+ if ((res = decode_frame(&p, c, data))) break;
+ if (input_read(in, data) || data->sz == 0) break;
+ }
+ return res;
+}
+
+static int decode_all(DemuxerContext *const in,
+ Dav1dContext *const c, Dav1dData *const data)
+{
+ int res = 0;
+ Dav1dPicture p;
+ do { if ((res = decode_frame(&p, c, data))) break;
+ } while (!input_read(in, data) && data->sz > 0);
+ return res;
+}
+
+static int seek(DemuxerContext *const in, Dav1dContext *const c,
+ const uint64_t pts, Dav1dData *const data)
+{
+ int res;
+ if ((res = input_seek(in, pts))) return res;
+ Dav1dSequenceHeader seq;
+ do { if ((res = input_read(in, data))) break;
+ } while (dav1d_parse_sequence_header(&seq, data->data, data->sz));
+ dav1d_flush(c);
+ return res;
+}
+
+int main(const int argc, char *const *const argv) {
+ const char *version = dav1d_version();
+ if (strcmp(version, DAV1D_VERSION)) {
+ fprintf(stderr, "Version mismatch (library: %s, executable: %s)\n",
+ version, DAV1D_VERSION);
+ return EXIT_FAILURE;
+ }
+
+ CLISettings cli_settings;
+ Dav1dSettings lib_settings;
+ DemuxerContext *in;
+ Dav1dContext *c;
+ Dav1dData data;
+ unsigned total, i_fps[2], i_timebase[2];
+ double timebase, spf, fps;
+ uint64_t pts;
+
+ xor128_srand(get_seed());
+ parse(argc, argv, &cli_settings, &lib_settings);
+
+ if (input_open(&in, "ivf", cli_settings.inputfile,
+ i_fps, &total, i_timebase) < 0 ||
+ !i_timebase[0] || !i_timebase[1] || !i_fps[0] || !i_fps[1])
+ {
+ return EXIT_SUCCESS;
+ }
+ if (dav1d_open(&c, &lib_settings))
+ return EXIT_FAILURE;
+
+ timebase = (double)i_timebase[1] / i_timebase[0];
+ spf = (double)i_fps[1] / i_fps[0];
+ fps = (double)i_fps[0] / i_fps[1];
+ if (fps < 1) goto end;
+
+#define FRAME_OFFSET_TO_PTS(foff) \
+ (uint64_t)llround(((foff) * spf) * 1000000000.0)
+#define TS_TO_PTS(ts) \
+ (uint64_t)llround(((ts) * timebase) * 1000000000.0)
+
+ // seek at random pts
+ for (int i = 0; i < NUM_RAND_SEEK; i++) {
+ pts = FRAME_OFFSET_TO_PTS(xor128_rand() % total);
+ if (seek(in, c, pts, &data)) continue;
+ if (decode_rand(in, c, &data, fps)) goto end;
+ }
+ pts = TS_TO_PTS(data.m.timestamp);
+
+ // seek left / right randomly with random intervals within 1s
+ for (int i = 0, tries = 0;
+ i - tries < NUM_REL_SEEK && tries < NUM_REL_SEEK / 2;
+ i++)
+ {
+ const int sign = xor128_rand() & 1 ? -1 : +1;
+ const float diff = (xor128_rand() % 100) / 100.f;
+ int64_t new_pts = pts + sign * FRAME_OFFSET_TO_PTS(diff * fps);
+ const int64_t new_ts = llround(new_pts / (timebase * 1000000000.0));
+ new_pts = TS_TO_PTS(new_ts);
+ if (new_pts < 0 || (uint64_t)new_pts >= FRAME_OFFSET_TO_PTS(total)) {
+ if (seek(in, c, FRAME_OFFSET_TO_PTS(total / 2), &data)) break;
+ pts = TS_TO_PTS(data.m.timestamp);
+ tries++;
+ continue;
+ }
+ if (seek(in, c, new_pts, &data))
+ if (seek(in, c, 0, &data)) goto end;
+ if (decode_rand(in, c, &data, fps)) goto end;
+ pts = TS_TO_PTS(data.m.timestamp);
+ }
+
+ unsigned shift = 0;
+ do {
+ shift += 5;
+ if (shift > total)
+ shift = total;
+ } while (seek(in, c, FRAME_OFFSET_TO_PTS(total - shift), &data));
+
+ // simulate seeking after the end of the file
+ for (int i = 0; i < NUM_END_SEEK; i++) {
+ if (seek(in, c, FRAME_OFFSET_TO_PTS(total - shift), &data)) goto end;
+ if (decode_all(in, c, &data)) goto end;
+ int num_flush = 1 + 64 + xor128_rand() % 64;
+ while (num_flush--) dav1d_flush(c);
+ }
+
+end:
+ input_close(in);
+ dav1d_close(&c);
+ return EXIT_SUCCESS;
+}