/*
 * Copyright © 2018, VideoLAN and dav1d authors
 * Copyright © 2018, Janne Grunau
 * Copyright © 2020, Martin Storsjo
 * 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 "src/arm/asm.S"
#include "util.S"

#define PREP_BIAS 8192

.macro avg d0, d00, d01, d1, d10, d11
        vld1.16         {q0, q1}, [r2, :128]!
        vld1.16         {q2, q3}, [r3, :128]!
        vqadd.s16       q0,  q0,  q2
        vqadd.s16       q1,  q1,  q3
        vmax.s16        q0,  q0,  q12 // -2*PREP_BIAS - 1 << intermediate_bits
        vmax.s16        q1,  q1,  q12 // -2*PREP_BIAS - 1 << intermediate_bits
        vqsub.s16       q0,  q0,  q12 // -2*PREP_BIAS - 1 << intermediate_bits
        vqsub.s16       q1,  q1,  q12 // -2*PREP_BIAS - 1 << intermediate_bits
        vshl.s16        \d0, q0,  q13 // -(intermediate_bits+1)
        vshl.s16        \d1, q1,  q13 // -(intermediate_bits+1)
.endm

.macro w_avg d0, d00, d01, d1, d10, d11
        vld1.16         {q0, q1}, [r2, :128]!
        vld1.16         {q2, q3}, [r3, :128]!
        // This difference requires a 17 bit range, and all bits are
        // significant for the following multiplication.
        vsubl.s16       \d0, d4,  d0
        vsubl.s16       q0,  d5,  d1
        vsubl.s16       \d1, d6,  d2
        vsubl.s16       q1,  d7,  d3
        vmul.s32        \d0, \d0, q4
        vmul.s32        q0,  q0,  q4
        vmul.s32        \d1, \d1, q4
        vmul.s32        q1,  q1,  q4
        vshr.s32        \d0, \d0, #4
        vshr.s32        q0,  q0,  #4
        vshr.s32        \d1, \d1, #4
        vshr.s32        q1,  q1,  #4
        vaddw.s16       \d0, \d0, d4
        vaddw.s16       q0,  q0,  d5
        vaddw.s16       \d1, \d1, d6
        vaddw.s16       q1,  q1,  d7
        vmovn.i32       \d00, \d0
        vmovn.i32       \d01, q0
        vmovn.i32       \d10, \d1
        vmovn.i32       \d11, q1
        vrshl.s16       \d0, \d0, q13 // -intermediate_bits
        vrshl.s16       \d1, \d1, q13 // -intermediate_bits
        vadd.s16        \d0, \d0, q12 // PREP_BIAS >> intermediate_bits
        vadd.s16        \d1, \d1, q12 // PREP_BIAS >> intermediate_bits
        vmin.s16        \d0, \d0, q15 // bitdepth_max
        vmin.s16        \d1, \d1, q15 // bitdepth_max
        vmax.s16        \d0, \d0, q14 // 0
        vmax.s16        \d1, \d1, q14 // 0
.endm

.macro mask d0, d00, d01, d1, d10, d11
        vld1.8          {q7},     [r6, :128]!
        vld1.16         {q0, q1}, [r2, :128]!
        vneg.s8         q7,  q7
        vld1.16         {q2, q3}, [r3, :128]!
        vmovl.s8        q6,  d14
        vmovl.s8        q7,  d15
        vmovl.s16       q4,  d12
        vmovl.s16       q5,  d13
        vmovl.s16       q6,  d14
        vmovl.s16       q7,  d15
        vsubl.s16       \d0, d4,  d0
        vsubl.s16       q0,  d5,  d1
        vsubl.s16       \d1, d6,  d2
        vsubl.s16       q1,  d7,  d3
        vmul.s32        \d0, \d0, q4
        vmul.s32        q0,  q0,  q5
        vmul.s32        \d1, \d1, q6
        vmul.s32        q1,  q1,  q7
        vshr.s32        \d0, \d0, #6
        vshr.s32        q0,  q0,  #6
        vshr.s32        \d1, \d1, #6
        vshr.s32        q1,  q1,  #6
        vaddw.s16       \d0, \d0, d4
        vaddw.s16       q0,  q0,  d5
        vaddw.s16       \d1, \d1, d6
        vaddw.s16       q1,  q1,  d7
        vmovn.i32       \d00, \d0
        vmovn.i32       \d01, q0
        vmovn.i32       \d10, \d1
        vmovn.i32       \d11, q1
        vrshl.s16       \d0, \d0, q13 // -intermediate_bits
        vrshl.s16       \d1, \d1, q13 // -intermediate_bits
        vadd.s16        \d0, \d0, q12 // PREP_BIAS >> intermediate_bits
        vadd.s16        \d1, \d1, q12 // PREP_BIAS >> intermediate_bits
        vmin.s16        \d0, \d0, q15 // bitdepth_max
        vmin.s16        \d1, \d1, q15 // bitdepth_max
        vmax.s16        \d0, \d0, q14 // 0
        vmax.s16        \d1, \d1, q14 // 0
.endm

.macro bidir_fn type, bdmax
function \type\()_16bpc_neon, export=1
        push            {r4-r7,lr}
        ldrd            r4,  r5,  [sp, #20]
        ldr             r6,  [sp, #28]
        clz             r4,  r4
.ifnc \type, avg
        ldr             r7,  [sp, #32]
        vmov.i16        q14, #0
        vdup.16         q15, r7         // bitdepth_max
.endif
.ifc \type, w_avg
        vpush           {q4}
.endif
.ifc \type, mask
        vpush           {q4-q7}
.endif
        clz             r7,  \bdmax
        sub             r7,  r7,  #18   // intermediate_bits = clz(bitdepth_max) - 18
.ifc \type, avg
        mov             lr,  #1
        movw            r12, #2*PREP_BIAS
        lsl             lr,  lr,  r7    // 1 << intermediate_bits
        neg             r12, r12         // -2*PREP_BIAS
        add             r7,  r7,  #1
        sub             r12, r12, lr    // -2*PREP_BIAS - 1 << intermediate_bits
        neg             r7,  r7         // -(intermediate_bits+1)
        vdup.16         q12, r12         // -2*PREP_BIAS - 1 << intermediate_bits
        vdup.16         q13, r7         // -(intermediate_bits+1)
.else
        mov             r12, #PREP_BIAS
        lsr             r12, r12, r7    // PREP_BIAS >> intermediate_bits
        neg             r7,  r7         // -intermediate_bits
        vdup.16         q12, r12         // PREP_BIAS >> intermediate_bits
        vdup.16         q13, r7         // -intermediate_bits
.endif
.ifc \type, w_avg
        vdup.32         q4,  r6
        vneg.s32        q4,  q4
.endif
        adr             r7,  L(\type\()_tbl)
        sub             r4,  r4,  #24
        \type           q8,  d16, d17, q9,  d18, d19
        ldr             r4,  [r7, r4, lsl #2]
        add             r7,  r7,  r4
        bx              r7

        .align 2
L(\type\()_tbl):
        .word 1280f - L(\type\()_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_tbl) + CONFIG_THUMB

40:
        add             r7,  r0,  r1
        lsl             r1,  r1,  #1
4:
        subs            r5,  r5,  #4
        vst1.16         {d16},  [r0, :64], r1
        vst1.16         {d17},  [r7, :64], r1
        vst1.16         {d18},  [r0, :64], r1
        vst1.16         {d19},  [r7, :64], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               4b
80:
        add             r7,  r0,  r1
        lsl             r1,  r1,  #1
8:
        vst1.16         {q8},  [r0, :128], r1
        subs            r5,  r5,  #2
        vst1.16         {q9},  [r7, :128], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               8b
160:
16:
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r1
        subs            r5,  r5,  #2
        vst1.16         {q10, q11}, [r0, :128], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               16b
320:
        add             r7,  r0,  #32
32:
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r1
        subs            r5,  r5,  #1
        vst1.16         {q10, q11}, [r7, :128], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               32b
640:
        add             r7,  r0,  #32
        mov             r12, #64
        sub             r1,  r1,  #64
64:
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r12
        \type           q8,  d16, d17, q9,  d18, d19
        vst1.16         {q10, q11}, [r7, :128], r12
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r1
        subs            r5,  r5,  #1
        vst1.16         {q10, q11}, [r7, :128], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               64b
1280:
        add             r7,  r0,  #32
        mov             r12, #64
        sub             r1,  r1,  #192
128:
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r12
        \type           q8,  d16, d17, q9,  d18, d19
        vst1.16         {q10, q11}, [r7, :128], r12
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r12
        \type           q8,  d16, d17, q9,  d18, d19
        vst1.16         {q10, q11}, [r7, :128], r12
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r12
        \type           q8,  d16, d17, q9,  d18, d19
        vst1.16         {q10, q11}, [r7, :128], r12
        \type           q10, d20, d21, q11, d22, d23
        vst1.16         {q8,  q9},  [r0, :128], r1
        subs            r5,  r5,  #1
        vst1.16         {q10, q11}, [r7, :128], r1
        ble             0f
        \type           q8,  d16, d17, q9,  d18, d19
        b               128b
0:
.ifc \type, mask
        vpop            {q4-q7}
.endif
.ifc \type, w_avg
        vpop            {q4}
.endif
        pop             {r4-r7,pc}
endfunc
.endm

bidir_fn avg, r6
bidir_fn w_avg, r7
bidir_fn mask, r7


.macro w_mask_fn type
function w_mask_\type\()_16bpc_neon, export=1
        push            {r4-r10,lr}
        vpush           {q4-q7}
        ldrd            r4,  r5,  [sp, #96]
        ldrd            r6,  r7,  [sp, #104]
        ldr             r8,  [sp, #112]
        clz             r9,  r4
        adr             lr,  L(w_mask_\type\()_tbl)
        vdup.16         q15, r8       // bitdepth_max
        sub             r9,  r9,  #24
        clz             r8,  r8       // clz(bitdepth_max)
        ldr             r9,  [lr,  r9,  lsl #2]
        add             r9,  lr,  r9
        sub             r8,  r8,  #12 // sh = intermediate_bits + 6 = clz(bitdepth_max) - 12
        mov             r10, #PREP_BIAS*64
        neg             r8,  r8       // -sh
        movw            r12, #27615   // (64 + 1 - 38)<<mask_sh - 1 - mask_rnd
        vdup.32         q14, r8       // -sh
        vdup.16         q0,  r12
.if \type == 444
        vmov.i8         q1,  #64
.elseif \type == 422
        vdup.8          d4,  r7
        vmov.i8         d2,  #129
        vsub.i16        d2,  d2,  d4
.elseif \type == 420
        vdup.16         q2,  r7
        vmov.i16        q1,  #0x100
        vsub.i16        q1,  q1,  q2
.endif
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
        bx              r9

        .align 2
L(w_mask_\type\()_tbl):
        .word 1280f - L(w_mask_\type\()_tbl) + CONFIG_THUMB
        .word 640f  - L(w_mask_\type\()_tbl) + CONFIG_THUMB
        .word 320f  - L(w_mask_\type\()_tbl) + CONFIG_THUMB
        .word 160f  - L(w_mask_\type\()_tbl) + CONFIG_THUMB
        .word 8f    - L(w_mask_\type\()_tbl) + CONFIG_THUMB
        .word 4f    - L(w_mask_\type\()_tbl) + CONFIG_THUMB

4:
        vld1.16         {q2, q3}, [r2, :128]! // tmp1 (four rows at once)
        vld1.16         {q4, q5}, [r3, :128]! // tmp2 (four rows at once)
        subs            r5,  r5,  #4
        vdup.32         q13, r10       // PREP_BIAS*64
        vabd.s16        q6,  q2,  q4   // abs(tmp1 - tmp2)
        vabd.s16        q7,  q3,  q5
        vsubl.s16       q8,  d8,  d4   // tmp2 - tmp1 (requires 17 bit)
        vsubl.s16       q9,  d9,  d5
        vsubl.s16       q10, d10, d6
        vsubl.s16       q11, d11, d7
        vqsub.u16       q6,  q0,  q6   // 27615 - abs()
        vqsub.u16       q7,  q0,  q7
        vshll.s16       q5,  d7,  #6   // tmp1 << 6
        vshll.s16       q4,  d6,  #6
        vshll.s16       q3,  d5,  #6
        vshll.s16       q2,  d4,  #6
        vshr.u16        q6,  q6,  #10  // 64-m = (27615 - abs()) >> mask_sh
        vshr.u16        q7,  q7,  #10
        vadd.i32        q2,  q2,  q13  // += PREP_BIAS*64
        vadd.i32        q3,  q3,  q13
        vadd.i32        q4,  q4,  q13
        vadd.i32        q5,  q5,  q13
        vmovl.u16       q12, d12
        vmovl.u16       q13, d13
        vmla.i32        q2,  q8,  q12  // (tmp2-tmp1)*(64-m)
        vmovl.u16       q12, d14
        vmla.i32        q3,  q9,  q13
        vmovl.u16       q13, d15
        vmla.i32        q4,  q10, q12
        vmla.i32        q5,  q11, q13
        vrshl.s32       q2,  q2,  q14  // (tmp1<<6 + (tmp2-tmp1)*(64-m) + (1 << (sh-1)) + PREP_BIAS*64) >> sh
        vrshl.s32       q3,  q3,  q14
        vrshl.s32       q4,  q4,  q14
        vrshl.s32       q5,  q5,  q14
        vqmovun.s32     d4,  q2        // iclip_pixel
        vqmovun.s32     d5,  q3
        vqmovun.s32     d6,  q4
        vqmovun.s32     d7,  q5
        vmin.u16        q2,  q2,  q15  // iclip_pixel
        vmin.u16        q3,  q3,  q15  // iclip_pixel
.if \type == 444
        vmovn.i16       d12, q6        // 64 - m
        vmovn.i16       d13, q7
        vsub.i16        q6,  q1,  q6   // m
        vst1.8          {q6}, [r6, :128]!
.elseif \type == 422
        vpadd.i16       d12, d12, d13  // (64 - m) + (64 - n) (column wise addition)
        vpadd.i16       d13, d14, d15
        vmovn.i16       d12, q6
        vhsub.u8        d12, d2,  d12  // ((129 - sign) - ((64 - m) + (64 - n)) >> 1
        vst1.8          {d12}, [r6, :64]!
.elseif \type == 420
        vadd.i16        d12, d12, d13  // (64 - my1) + (64 - my2) (row wise addition)
        vadd.i16        d13, d14, d15
        vpadd.i16       d12, d12, d13  // (128 - m) + (128 - n) (column wise addition)
        vsub.i16        d12, d2,  d12  // (256 - sign) - ((128 - m) + (128 - n))
        vrshrn.i16      d12, q6,  #2   // ((256 - sign) - ((128 - m) + (128 - n)) + 2) >> 2
        vst1.32         {d12[0]}, [r6, :32]!
.endif
        vst1.16         {d4}, [r0,  :64], r1
        vst1.16         {d5}, [r12, :64], r1
        vst1.16         {d6}, [r0,  :64], r1
        vst1.16         {d7}, [r12, :64], r1
        bgt             4b
        vpop            {q4-q7}
        pop             {r4-r10,pc}
8:
        vld1.16         {q2, q3}, [r2, :128]! // tmp1
        vld1.16         {q4, q5}, [r3, :128]! // tmp2
        subs            r5,  r5,  #2
        vdup.32         q13, r10       // PREP_BIAS*64
        vabd.s16        q6,  q2,  q4   // abs(tmp1 - tmp2)
        vabd.s16        q7,  q3,  q5
        vsubl.s16       q8,  d8,  d4   // tmp2 - tmp1 (requires 17 bit)
        vsubl.s16       q9,  d9,  d5
        vsubl.s16       q10, d10, d6
        vsubl.s16       q11, d11, d7
        vqsub.u16       q6,  q0,  q6   // 27615 - abs()
        vqsub.u16       q7,  q0,  q7
        vshll.s16       q5,  d7,  #6   // tmp1 << 6
        vshll.s16       q4,  d6,  #6
        vshll.s16       q3,  d5,  #6
        vshll.s16       q2,  d4,  #6
        vshr.u16        q6,  q6,  #10  // 64-m = (27615 - abs()) >> mask_sh
        vshr.u16        q7,  q7,  #10
        vadd.i32        q2,  q2,  q13  // += PREP_BIAS*64
        vadd.i32        q3,  q3,  q13
        vadd.i32        q4,  q4,  q13
        vadd.i32        q5,  q5,  q13
        vmovl.u16       q12, d12
        vmovl.u16       q13, d13
        vmla.i32        q2,  q8,  q12  // (tmp2-tmp1)*(64-m)
        vmovl.u16       q12, d14
        vmla.i32        q3,  q9,  q13
        vmovl.u16       q13, d15
        vmla.i32        q4,  q10, q12
        vmla.i32        q5,  q11, q13
        vrshl.s32       q2,  q2,  q14  // (tmp1<<6 + (tmp2-tmp1)*(64-m) + (1 << (sh-1)) + PREP_BIAS*64) >> sh
        vrshl.s32       q3,  q3,  q14
        vrshl.s32       q4,  q4,  q14
        vrshl.s32       q5,  q5,  q14
        vqmovun.s32     d4,  q2        // iclip_pixel
        vqmovun.s32     d5,  q3
        vqmovun.s32     d6,  q4
        vqmovun.s32     d7,  q5
        vmin.u16        q2,  q2,  q15  // iclip_pixel
        vmin.u16        q3,  q3,  q15  // iclip_pixel
.if \type == 444
        vmovn.i16       d12, q6        // 64 - m
        vmovn.i16       d13, q7
        vsub.i16        q6,  q1,  q6   // m
        vst1.8          {q6}, [r6, :128]!
.elseif \type == 422
        vpadd.i16       d12, d12, d13  // (64 - m) + (64 - n) (column wise addition)
        vpadd.i16       d13, d14, d15
        vmovn.i16       d12, q6
        vhsub.u8        d12, d2,  d12  // ((129 - sign) - ((64 - m) + (64 - n)) >> 1
        vst1.8          {d12}, [r6, :64]!
.elseif \type == 420
        vadd.i16        q6,  q6,  q7   // (64 - my1) + (64 - my2) (row wise addition)
        vpadd.i16       d12, d12, d13  // (128 - m) + (128 - n) (column wise addition)
        vsub.i16        d12, d2,  d12  // (256 - sign) - ((128 - m) + (128 - n))
        vrshrn.i16      d12, q6,  #2   // ((256 - sign) - ((128 - m) + (128 - n)) + 2) >> 2
        vst1.32         {d12[0]}, [r6, :32]!
.endif
        vst1.16         {q2}, [r0,  :128], r1
        vst1.16         {q3}, [r12, :128], r1
        bgt             8b
        vpop            {q4-q7}
        pop             {r4-r10,pc}
1280:
640:
320:
160:
        sub             r1,  r1,  r4,  lsl #1
.if \type == 444
        add             lr,  r6,  r4
.elseif \type == 422
        add             lr,  r6,  r4,  lsr #1
.endif
        add             r7,  r2,  r4,  lsl #1
        add             r9,  r3,  r4,  lsl #1
161:
        mov             r8,  r4
16:
        vld1.16         {q2}, [r2, :128]! // tmp1
        vld1.16         {q4}, [r3, :128]! // tmp2
        vld1.16         {q3}, [r7, :128]!
        vld1.16         {q5}, [r9, :128]!
        subs            r8,  r8,  #8
        vdup.32         q13, r10       // PREP_BIAS*64
        vabd.s16        q6,  q2,  q4   // abs(tmp1 - tmp2)
        vabd.s16        q7,  q3,  q5
        vsubl.s16       q8,  d8,  d4   // tmp2 - tmp1 (requires 17 bit)
        vsubl.s16       q9,  d9,  d5
        vsubl.s16       q10, d10, d6
        vsubl.s16       q11, d11, d7
        vqsub.u16       q6,  q0,  q6   // 27615 - abs()
        vqsub.u16       q7,  q0,  q7
        vshll.s16       q5,  d7,  #6   // tmp1 << 6
        vshll.s16       q4,  d6,  #6
        vshll.s16       q3,  d5,  #6
        vshll.s16       q2,  d4,  #6
        vshr.u16        q6,  q6,  #10  // 64-m = (27615 - abs()) >> mask_sh
        vshr.u16        q7,  q7,  #10
        vadd.i32        q2,  q2,  q13  // += PREP_BIAS*64
        vadd.i32        q3,  q3,  q13
        vadd.i32        q4,  q4,  q13
        vadd.i32        q5,  q5,  q13
        vmovl.u16       q12, d12
        vmovl.u16       q13, d13
        vmla.i32        q2,  q8,  q12  // (tmp2-tmp1)*(64-m)
        vmovl.u16       q12, d14
        vmla.i32        q3,  q9,  q13
        vmovl.u16       q13, d15
        vmla.i32        q4,  q10, q12
        vmla.i32        q5,  q11, q13
        vrshl.s32       q2,  q2,  q14  // (tmp1<<6 + (tmp2-tmp1)*(64-m) + (1 << (sh-1)) + PREP_BIAS*64) >> sh
        vrshl.s32       q3,  q3,  q14
        vrshl.s32       q4,  q4,  q14
        vrshl.s32       q5,  q5,  q14
        vqmovun.s32     d4,  q2        // iclip_pixel
        vqmovun.s32     d5,  q3
        vqmovun.s32     d6,  q4
        vqmovun.s32     d7,  q5
        vmin.u16        q2,  q2,  q15  // iclip_pixel
        vmin.u16        q3,  q3,  q15  // iclip_pixel
.if \type == 444
        vmovn.i16       d12, q6        // 64 - m
        vmovn.i16       d13, q7
        vsub.i16        q6,  q1,  q6   // m
        vst1.8          {d12}, [r6, :64]!
        vst1.8          {d13}, [lr, :64]!
.elseif \type == 422
        vpadd.i16       d12, d12, d13  // (64 - m) + (64 - n) (column wise addition)
        vpadd.i16       d13, d14, d15
        vmovn.i16       d12, q6
        vhsub.u8        d12, d2,  d12  // ((129 - sign) - ((64 - m) + (64 - n)) >> 1
        vst1.32         {d12[0]}, [r6, :32]!
        vst1.32         {d12[1]}, [lr, :32]!
.elseif \type == 420
        vadd.i16        q6,  q6,  q7   // (64 - my1) + (64 - my2) (row wise addition)
        vpadd.i16       d12, d12, d13  // (128 - m) + (128 - n) (column wise addition)
        vsub.i16        d12, d2,  d12  // (256 - sign) - ((128 - m) + (128 - n))
        vrshrn.i16      d12, q6,  #2   // ((256 - sign) - ((128 - m) + (128 - n)) + 2) >> 2
        vst1.32         {d12[0]}, [r6, :32]!
.endif
        vst1.16         {q2}, [r0,  :128]!
        vst1.16         {q3}, [r12, :128]!
        bgt             16b
        subs            r5,  r5,  #2
        add             r2,  r2,  r4,  lsl #1
        add             r3,  r3,  r4,  lsl #1
        add             r7,  r7,  r4,  lsl #1
        add             r9,  r9,  r4,  lsl #1
.if \type == 444
        add             r6,  r6,  r4
        add             lr,  lr,  r4
.elseif \type == 422
        add             r6,  r6,  r4,  lsr #1
        add             lr,  lr,  r4,  lsr #1
.endif
        add             r0,  r0,  r1
        add             r12, r12, r1
        bgt             161b
        vpop            {q4-q7}
        pop             {r4-r10,pc}
endfunc
.endm

w_mask_fn 444
w_mask_fn 422
w_mask_fn 420

function blend_16bpc_neon, export=1
        push            {r4-r5,lr}
        ldrd            r4,  r5,  [sp, #12]
        clz             lr,  r3
        adr             r3,  L(blend_tbl)
        sub             lr,  lr,  #26
        ldr             lr,  [r3,  lr,  lsl #2]
        add             r3,  r3,  lr
        bx              r3

        .align 2
L(blend_tbl):
        .word 320f - L(blend_tbl) + CONFIG_THUMB
        .word 160f - L(blend_tbl) + CONFIG_THUMB
        .word 80f  - L(blend_tbl) + CONFIG_THUMB
        .word 40f  - L(blend_tbl) + CONFIG_THUMB

40:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
4:
        vld1.8          {d4}, [r5, :64]!
        vld1.16         {q1}, [r2, :128]!
        vld1.16         {d0}, [r0, :64]
        vneg.s8         d4,  d4       // -m
        subs            r4,  r4,  #2
        vld1.16         {d1}, [r12, :64]
        vmovl.s8        q2,  d4
        vshl.i16        q2,  q2,  #9  // -m << 9
        vsub.i16        q1,  q0,  q1  // a - b
        vqrdmulh.s16    q1,  q1,  q2  // ((a-b)*-m + 32) >> 6
        vadd.i16        q0,  q0,  q1
        vst1.16         {d0}, [r0,  :64], r1
        vst1.16         {d1}, [r12, :64], r1
        bgt             4b
        pop             {r4-r5,pc}
80:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
8:
        vld1.8          {q8},     [r5, :128]!
        vld1.16         {q2, q3}, [r2, :128]!
        vneg.s8         q9,  q8       // -m
        vld1.16         {q0},     [r0,  :128]
        vld1.16         {q1},     [r12, :128]
        vmovl.s8        q8,  d18
        vmovl.s8        q9,  d19
        vshl.i16        q8,  q8,  #9  // -m << 9
        vshl.i16        q9,  q9,  #9
        vsub.i16        q2,  q0,  q2  // a - b
        vsub.i16        q3,  q1,  q3
        subs            r4,  r4,  #2
        vqrdmulh.s16    q2,  q2,  q8  // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q3,  q3,  q9
        vadd.i16        q0,  q0,  q2
        vadd.i16        q1,  q1,  q3
        vst1.16         {q0}, [r0,  :128], r1
        vst1.16         {q1}, [r12, :128], r1
        bgt             8b
        pop             {r4-r5,pc}
160:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
16:
        vld1.8          {q12, q13}, [r5, :128]!
        vld1.16         {q8,  q9},  [r2, :128]!
        subs            r4,  r4,  #2
        vneg.s8         q14, q12      // -m
        vld1.16         {q0,  q1},  [r0, :128]
        vneg.s8         q15, q13
        vld1.16         {q10, q11}, [r2, :128]!
        vmovl.s8        q12, d28
        vmovl.s8        q13, d29
        vmovl.s8        q14, d30
        vmovl.s8        q15, d31
        vld1.16         {q2,  q3},  [r12, :128]
        vshl.i16        q12, q12, #9  // -m << 9
        vshl.i16        q13, q13, #9
        vshl.i16        q14, q14, #9
        vshl.i16        q15, q15, #9
        vsub.i16        q8,  q0,  q8  // a - b
        vsub.i16        q9,  q1,  q9
        vsub.i16        q10, q2,  q10
        vsub.i16        q11, q3,  q11
        vqrdmulh.s16    q8,  q8,  q12 // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q9,  q9,  q13
        vqrdmulh.s16    q10, q10, q14
        vqrdmulh.s16    q11, q11, q15
        vadd.i16        q0,  q0,  q8
        vadd.i16        q1,  q1,  q9
        vadd.i16        q2,  q2,  q10
        vst1.16         {q0, q1}, [r0,  :128], r1
        vadd.i16        q3,  q3,  q11
        vst1.16         {q2, q3}, [r12, :128], r1
        bgt             16b
        pop             {r4-r5,pc}
320:
        add             r12, r0,  #32
32:
        vld1.8          {q12, q13}, [r5, :128]!
        vld1.16         {q8,  q9},  [r2, :128]!
        subs            r4,  r4,  #1
        vneg.s8         q14, q12      // -m
        vld1.16         {q0,  q1},  [r0, :128]
        vneg.s8         q15, q13
        vld1.16         {q10, q11}, [r2, :128]!
        vmovl.s8        q12, d28
        vmovl.s8        q13, d29
        vmovl.s8        q14, d30
        vmovl.s8        q15, d31
        vld1.16         {q2,  q3},  [r12, :128]
        vshl.i16        q12, q12, #9  // -m << 9
        vshl.i16        q13, q13, #9
        vshl.i16        q14, q14, #9
        vshl.i16        q15, q15, #9
        vsub.i16        q8,  q0,  q8  // a - b
        vsub.i16        q9,  q1,  q9
        vsub.i16        q10, q2,  q10
        vsub.i16        q11, q3,  q11
        vqrdmulh.s16    q8,  q8,  q12 // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q9,  q9,  q13
        vqrdmulh.s16    q10, q10, q14
        vqrdmulh.s16    q11, q11, q15
        vadd.i16        q0,  q0,  q8
        vadd.i16        q1,  q1,  q9
        vadd.i16        q2,  q2,  q10
        vst1.16         {q0, q1}, [r0,  :128], r1
        vadd.i16        q3,  q3,  q11
        vst1.16         {q2, q3}, [r12, :128], r1
        bgt             32b
        pop             {r4-r5,pc}
endfunc

function blend_h_16bpc_neon, export=1
        push            {r4-r5,lr}
        ldr             r4,  [sp, #12]
        movrel          r5,  X(obmc_masks)
        add             r5,  r5,  r4
        sub             r4,  r4,  r4,  lsr #2
        clz             lr,  r3
        adr             r12, L(blend_h_tbl)
        sub             lr,  lr,  #24
        ldr             lr,  [r12, lr,  lsl #2]
        add             r12, r12, lr
        bx              r12

        .align 2
L(blend_h_tbl):
        .word 1280f - L(blend_h_tbl) + CONFIG_THUMB
        .word 640f  - L(blend_h_tbl) + CONFIG_THUMB
        .word 320f  - L(blend_h_tbl) + CONFIG_THUMB
        .word 160f  - L(blend_h_tbl) + CONFIG_THUMB
        .word 80f   - L(blend_h_tbl) + CONFIG_THUMB
        .word 40f   - L(blend_h_tbl) + CONFIG_THUMB
        .word 20f   - L(blend_h_tbl) + CONFIG_THUMB

20:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
2:
        vld2.8          {d4[], d5[]}, [r5, :16]!
        vld1.16         {d2},         [r2, :64]!
        vext.8          d4,  d4,  d5,  #6
        subs            r4,  r4,  #2
        vneg.s8         d4,  d4       // -m
        vld1.32         {d0[]},  [r0, :32]
        vld1.32         {d0[1]}, [r12, :32]
        vmovl.s8        q2,  d4
        vshl.i16        d4,  d4,  #9  // -m << 9
        vsub.i16        d2,  d0,  d2  // a - b
        vqrdmulh.s16    d2,  d2,  d4  // ((a-b)*-m + 32) >> 6
        vadd.i16        d0,  d0,  d2
        vst1.32         {d0[0]}, [r0,  :32], r1
        vst1.32         {d0[1]}, [r12, :32], r1
        bgt             2b
        pop             {r4-r5,pc}
40:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
4:
        vld2.8          {d4[], d5[]}, [r5, :16]!
        vld1.16         {q1},         [r2, :128]!
        vext.8          d4,  d4,  d5,  #4
        subs            r4,  r4,  #2
        vneg.s8         d4,  d4       // -m
        vld1.16         {d0}, [r0,  :64]
        vld1.16         {d1}, [r12, :64]
        vmovl.s8        q2,  d4
        vshl.i16        q2,  q2,  #9  // -m << 9
        vsub.i16        q1,  q0,  q1  // a - b
        vqrdmulh.s16    q1,  q1,  q2  // ((a-b)*-m + 32) >> 6
        vadd.i16        q0,  q0,  q1
        vst1.16         {d0}, [r0,  :64], r1
        vst1.16         {d1}, [r12, :64], r1
        bgt             4b
        pop             {r4-r5,pc}
80:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
8:
        vld2.8          {d16[], d17[]}, [r5, :16]!
        vld1.16         {q2, q3},       [r2, :128]!
        vneg.s8         q9,  q8      // -m
        vld1.16         {q0}, [r0, :128]
        subs            r4,  r4,  #2
        vmovl.s8        q8,  d18
        vmovl.s8        q9,  d19
        vld1.16         {q1}, [r12, :128]
        vshl.i16        q8,  q8,  #9  // -m << 9
        vshl.i16        q9,  q9,  #9
        vsub.i16        q2,  q0,  q2  // a - b
        vsub.i16        q3,  q1,  q3
        vqrdmulh.s16    q2,  q2,  q8  // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q3,  q3,  q9
        vadd.i16        q0,  q0,  q2
        vadd.i16        q1,  q1,  q3
        vst1.16         {q0}, [r0,  :128], r1
        vst1.16         {q1}, [r12, :128], r1
        bgt             8b
        pop             {r4-r5,pc}
160:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
16:
        vld2.8          {d24[], d25[]}, [r5, :16]!
        vld1.16         {q8,  q9},  [r2, :128]!
        subs            r4,  r4,  #2
        vneg.s8         q13, q12      // -m
        vld1.16         {q0,  q1},  [r0, :128]
        vmovl.s8        q12, d26
        vld1.16         {q10, q11}, [r2, :128]!
        vmovl.s8        q13, d27
        vld1.16         {q2,  q3},  [r12, :128]
        vshl.i16        q12, q12, #9  // -m << 9
        vshl.i16        q13, q13, #9
        vsub.i16        q8,  q0,  q8  // a - b
        vsub.i16        q9,  q1,  q9
        vsub.i16        q10, q2,  q10
        vsub.i16        q11, q3,  q11
        vqrdmulh.s16    q8,  q8,  q12 // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q9,  q9,  q12
        vqrdmulh.s16    q10, q10, q13
        vqrdmulh.s16    q11, q11, q13
        vadd.i16        q0,  q0,  q8
        vadd.i16        q1,  q1,  q9
        vadd.i16        q2,  q2,  q10
        vadd.i16        q3,  q3,  q11
        vst1.16         {q0, q1}, [r0,  :128], r1
        vst1.16         {q2, q3}, [r12, :128], r1
        bgt             16b
        pop             {r4-r5,pc}
1280:
640:
320:
        sub             r1,  r1,  r3,  lsl #1
321:
        vld1.8          {d24[]}, [r5]!
        mov             r12, r3
        vneg.s8         d24, d24      // -m
        vmovl.s8        q12, d24
        vshl.i16        q12, q12, #9  // -m << 9
32:
        vld1.16         {q8,  q9},  [r2, :128]!
        vld1.16         {q0,  q1},  [r0, :128]!
        subs            r12, r12, #32
        vld1.16         {q10, q11}, [r2, :128]!
        vld1.16         {q2,  q3},  [r0, :128]
        vsub.i16        q8,  q0,  q8  // a - b
        vsub.i16        q9,  q1,  q9
        vsub.i16        q10, q2,  q10
        vsub.i16        q11, q3,  q11
        sub             r0,  r0,  #32
        vqrdmulh.s16    q8,  q8,  q12 // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q9,  q9,  q12
        vqrdmulh.s16    q10, q10, q12
        vqrdmulh.s16    q11, q11, q12
        vadd.i16        q0,  q0,  q8
        vadd.i16        q1,  q1,  q9
        vadd.i16        q2,  q2,  q10
        vst1.16         {q0, q1}, [r0, :128]!
        vadd.i16        q3,  q3,  q11
        vst1.16         {q2, q3}, [r0, :128]!
        bgt             32b
        subs            r4,  r4,  #1
        add             r0,  r0,  r1
        bgt             321b
        pop             {r4-r5,pc}
endfunc

function blend_v_16bpc_neon, export=1
        push            {r4,lr}
        ldr             r4,  [sp, #8]
        movrel          lr,  X(obmc_masks)
        add             lr,  lr,  r3
        clz             r12, r3
        adr             r3,  L(blend_v_tbl)
        sub             r12, r12, #26
        ldr             r12, [r3,  r12, lsl #2]
        add             r3,  r3,  r12
        bx              r3

        .align 2
L(blend_v_tbl):
        .word 320f - L(blend_v_tbl) + CONFIG_THUMB
        .word 160f - L(blend_v_tbl) + CONFIG_THUMB
        .word 80f  - L(blend_v_tbl) + CONFIG_THUMB
        .word 40f  - L(blend_v_tbl) + CONFIG_THUMB
        .word 20f  - L(blend_v_tbl) + CONFIG_THUMB

20:
        add             r12, r0,  r1
        lsl             r1,  r1,  #1
        vld1.8          {d4[]}, [lr]
        vneg.s8         d4,  d4       // -m
        vmovl.s8        q2,  d4
        vshl.i16        d4,  d4,  #9  // -m << 9
2:
        vld1.32         {d2[]},  [r2, :32]!
        vld1.16         {d0[]},  [r0, :16]
        subs            r4,  r4,  #2
        vld1.16         {d2[1]}, [r2,  :16]
        vld1.16         {d0[1]}, [r12, :16]
        add             r2,  r2,  #4
        vsub.i16        d2,  d0,  d2  // a - b
        vqrdmulh.s16    d2,  d2,  d4  // ((a-b)*-m + 32) >> 6
        vadd.i16        d0,  d0,  d2
        vst1.16         {d0[0]}, [r0,  :16], r1
        vst1.16         {d0[1]}, [r12, :16], r1
        bgt             2b
        pop             {r4,pc}
40:
        vld1.32         {d4[]}, [lr, :32]
        add             r12, r0,  r1
        vneg.s8         d4,  d4       // -m
        lsl             r1,  r1,  #1
        vmovl.s8        q2,  d4
        sub             r1,  r1,  #4
        vshl.i16        q2,  q2,  #9  // -m << 9
4:
        vld1.16         {q1}, [r2, :128]!
        vld1.16         {d0}, [r0,  :64]
        vld1.16         {d1}, [r12, :64]
        subs            r4,  r4,  #2
        vsub.i16        q1,  q0,  q1  // a - b
        vqrdmulh.s16    q1,  q1,  q2  // ((a-b)*-m + 32) >> 6
        vadd.i16        q0,  q0,  q1
        vst1.32         {d0[0]}, [r0,  :32]!
        vst1.32         {d1[0]}, [r12, :32]!
        vst1.16         {d0[2]}, [r0,  :16], r1
        vst1.16         {d1[2]}, [r12, :16], r1
        bgt             4b
        pop             {r4,pc}
80:
        vld1.8          {d16}, [lr, :64]
        add             r12, r0,  r1
        vneg.s8         d16, d16      // -m
        lsl             r1,  r1,  #1
        vmovl.s8        q8,  d16
        sub             r1,  r1,  #8
        vshl.i16        q8,  q8,  #9  // -m << 9
8:
        vld1.16         {q2, q3}, [r2,  :128]!
        vld1.16         {q0},     [r0,  :128]
        vld1.16         {q1},     [r12, :128]
        subs            r4,  r4,  #2
        vsub.i16        q2,  q0,  q2  // a - b
        vsub.i16        q3,  q1,  q3
        vqrdmulh.s16    q2,  q2,  q8  // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q3,  q3,  q8
        vadd.i16        q0,  q0,  q2
        vadd.i16        q1,  q1,  q3
        vst1.16         {d0},    [r0,  :64]!
        vst1.16         {d2},    [r12, :64]!
        vst1.32         {d1[0]}, [r0,  :32], r1
        vst1.32         {d3[0]}, [r12, :32], r1
        bgt             8b
        pop             {r4,pc}
160:
        vld1.8          {q12}, [lr, :128]
        add             r12, r0,  r1
        vneg.s8         q13, q12      // -m
        lsl             r1,  r1,  #1
        vmovl.s8        q12, d26
        vmovl.s8        q13, d27
        vshl.i16        q12, q12, #9  // -m << 9
        vshl.i16        d26, d26, #9
16:
        vld1.16         {q8,  q9},      [r2,  :128]!
        vld1.16         {d0,  d1,  d2}, [r0,  :64]
        subs            r4,  r4,  #2
        vld1.16         {q10, q11},     [r2,  :128]!
        vsub.i16        q8,  q0,  q8  // a - b
        vld1.16         {d4,  d5,  d6}, [r12, :64]
        vsub.i16        d18, d2,  d18
        vsub.i16        q10, q2,  q10
        vsub.i16        d22, d6,  d22
        vqrdmulh.s16    q8,  q8,  q12  // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    d18, d18, d26
        vqrdmulh.s16    q10, q10, q12
        vqrdmulh.s16    d22, d22, d26
        vadd.i16        q0,  q0,  q8
        vadd.i16        d2,  d2,  d18
        vadd.i16        q2,  q2,  q10
        vst1.16         {d0,  d1,  d2}, [r0,  :64], r1
        vadd.i16        d6,  d6,  d22
        vst1.16         {d4,  d5,  d6}, [r12, :64], r1
        bgt             16b
        pop             {r4,pc}
320:
        vld1.8          {d24, d25, d26}, [lr, :64]
        vneg.s8         q14, q12      // -m
        vneg.s8         d30, d26
        vmovl.s8        q12, d28
        vmovl.s8        q13, d29
        vmovl.s8        q14, d30
        sub             r1,  r1,  #32
        vshl.i16        q12, q12, #9  // -m << 9
        vshl.i16        q13, q13, #9
        vshl.i16        q14, q14, #9
32:
        vld1.16         {q8,  q9},  [r2, :128]!
        vld1.16         {q0,  q1},  [r0, :128]!
        subs            r4,  r4,  #1
        vld1.16         {q10},      [r2, :128]
        vsub.i16        q8,  q0,  q8  // a - b
        vld1.16         {q2},       [r0, :128]
        sub             r0,  r0,  #32
        vsub.i16        q9,  q1,  q9
        vsub.i16        q10, q2,  q10
        vqrdmulh.s16    q8,  q8,  q12  // ((a-b)*-m + 32) >> 6
        vqrdmulh.s16    q9,  q9,  q13
        vqrdmulh.s16    q10, q10, q14
        vadd.i16        q0,  q0,  q8
        vadd.i16        q1,  q1,  q9
        vadd.i16        q2,  q2,  q10
        vst1.16         {q0, q1}, [r0, :128]!
        add             r2,  r2,  #32
        vst1.16         {q2},     [r0, :128], r1
        bgt             32b
        pop             {r4,pc}
endfunc

// This has got the same signature as the put_8tap functions,
// and assumes that r9 is set to (clz(w)-24).
function put_neon
        adr             r10, L(put_tbl)
        ldr             r9,  [r10, r9, lsl #2]
        add             r10, r10, r9
        bx              r10

        .align 2
L(put_tbl):
        .word 1280f - L(put_tbl) + CONFIG_THUMB
        .word 640f  - L(put_tbl) + CONFIG_THUMB
        .word 320f  - L(put_tbl) + CONFIG_THUMB
        .word 16f   - L(put_tbl) + CONFIG_THUMB
        .word 80f   - L(put_tbl) + CONFIG_THUMB
        .word 4f    - L(put_tbl) + CONFIG_THUMB
        .word 2f    - L(put_tbl) + CONFIG_THUMB

2:
        vld1.32         {d0[]}, [r2], r3
        vld1.32         {d1[]}, [r2], r3
        subs            r5,  r5,  #2
        vst1.32         {d0[0]}, [r0, :32], r1
        vst1.32         {d1[1]}, [r0, :32], r1
        bgt             2b
        pop             {r4-r11,pc}
4:
        vld1.16         {d0}, [r2], r3
        vld1.16         {d1}, [r2], r3
        subs            r5,  r5,  #2
        vst1.16         {d0}, [r0, :64], r1
        vst1.16         {d1}, [r0, :64], r1
        bgt             4b
        pop             {r4-r11,pc}
80:
        add             r8,  r0,  r1
        lsl             r1,  r1,  #1
        add             r9,  r2,  r3
        lsl             r3,  r3,  #1
8:
        vld1.16         {q0}, [r2], r3
        vld1.16         {q1}, [r9], r3
        subs            r5,  r5,  #2
        vst1.16         {q0}, [r0, :128], r1
        vst1.16         {q1}, [r8, :128], r1
        bgt             8b
        pop             {r4-r11,pc}
16:
        vld1.16         {q0,  q1},  [r2], r3
        subs            r5,  r5,  #1
        vst1.16         {q0,  q1},  [r0, :128], r1
        bgt             16b
        pop             {r4-r11,pc}
320:
        sub             r1,  r1,  #32
        sub             r3,  r3,  #32
32:
        vld1.16         {q0,  q1},  [r2]!
        vst1.16         {q0,  q1},  [r0, :128]!
        vld1.16         {q2,  q3},  [r2], r3
        subs            r5,  r5,  #1
        vst1.16         {q2,  q3},  [r0, :128], r1
        bgt             32b
        pop             {r4-r11,pc}
640:
        sub             r1,  r1,  #96
        sub             r3,  r3,  #96
64:
        vld1.16         {q8,  q9},  [r2]!
        vst1.16         {q8,  q9},  [r0, :128]!
        vld1.16         {q10, q11}, [r2]!
        vst1.16         {q10, q11}, [r0, :128]!
        vld1.16         {q12, q13}, [r2]!
        vst1.16         {q12, q13}, [r0, :128]!
        vld1.16         {q14, q15}, [r2], r3
        subs            r5,  r5,  #1
        vst1.16         {q14, q15}, [r0, :128], r1
        bgt             64b
        pop             {r4-r11,pc}
1280:
        sub             r1,  r1,  #224
        sub             r3,  r3,  #224
128:
        vld1.16         {q8,  q9},  [r2]!
        vst1.16         {q8,  q9},  [r0, :128]!
        vld1.16         {q10, q11}, [r2]!
        vst1.16         {q10, q11}, [r0, :128]!
        vld1.16         {q12, q13}, [r2]!
        vst1.16         {q12, q13}, [r0, :128]!
        vld1.16         {q14, q15}, [r2]!
        vst1.16         {q14, q15}, [r0, :128]!
        vld1.16         {q8,  q9},  [r2]!
        vst1.16         {q8,  q9},  [r0, :128]!
        vld1.16         {q10, q11}, [r2]!
        vst1.16         {q10, q11}, [r0, :128]!
        vld1.16         {q12, q13}, [r2]!
        vst1.16         {q12, q13}, [r0, :128]!
        vld1.16         {q14, q15}, [r2], r3
        subs            r5,  r5,  #1
        vst1.16         {q14, q15}, [r0, :128], r1
        bgt             128b
        pop             {r4-r11,pc}
endfunc

// This has got the same signature as the prep_8tap functions,
// and assumes that r9 is set to (clz(w)-24), r7 to intermediate_bits and
// r8 to w*2.
function prep_neon
        adr             r10, L(prep_tbl)
        ldr             r9,  [r10, r9, lsl #2]
        vdup.16         q15, r7   // intermediate_bits
        vmov.i16        q14, #PREP_BIAS
        add             r10, r10, r9
        bx              r10

        .align 2
L(prep_tbl):
        .word 1280f - L(prep_tbl) + CONFIG_THUMB
        .word 640f  - L(prep_tbl) + CONFIG_THUMB
        .word 320f  - L(prep_tbl) + CONFIG_THUMB
        .word 16f   - L(prep_tbl) + CONFIG_THUMB
        .word 80f   - L(prep_tbl) + CONFIG_THUMB
        .word 40f   - L(prep_tbl) + CONFIG_THUMB

40:
        add             r9,  r1,  r2
        lsl             r2,  r2,  #1
4:
        vld1.16         {d0}, [r1], r2
        vld1.16         {d1}, [r9], r2
        subs            r4,  r4,  #2
        vshl.s16        q0,  q0,  q15
        vsub.i16        q0,  q0,  q14
        vst1.16         {q0}, [r0, :128]!
        bgt             4b
        pop             {r4-r11,pc}
80:
        add             r9,  r1,  r2
        lsl             r2,  r2,  #1
8:
        vld1.16         {q0}, [r1], r2
        vld1.16         {q1}, [r9], r2
        subs            r4,  r4,  #2
        vshl.s16        q0,  q0,  q15
        vshl.s16        q1,  q1,  q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vst1.16         {q0, q1}, [r0, :128]!
        bgt             8b
        pop             {r4-r11,pc}
16:
        vld1.16         {q0, q1}, [r1], r2
        vshl.s16        q0,  q0,  q15
        vld1.16         {q2, q3}, [r1], r2
        subs            r4,  r4,  #2
        vshl.s16        q1,  q1,  q15
        vshl.s16        q2,  q2,  q15
        vshl.s16        q3,  q3,  q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vsub.i16        q2,  q2,  q14
        vst1.16         {q0, q1}, [r0, :128]!
        vsub.i16        q3,  q3,  q14
        vst1.16         {q2, q3}, [r0, :128]!
        bgt             16b
        pop             {r4-r11,pc}
320:
        sub             r2,  r2,  #32
32:
        vld1.16         {q0, q1}, [r1]!
        subs            r4,  r4,  #1
        vshl.s16        q0,  q0,  q15
        vld1.16         {q2, q3}, [r1], r2
        vshl.s16        q1,  q1,  q15
        vshl.s16        q2,  q2,  q15
        vshl.s16        q3,  q3,  q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vsub.i16        q2,  q2,  q14
        vst1.16         {q0, q1}, [r0, :128]!
        vsub.i16        q3,  q3,  q14
        vst1.16         {q2, q3}, [r0, :128]!
        bgt             32b
        pop             {r4-r11,pc}
640:
        sub             r2,  r2,  #96
64:
        vld1.16         {q0,  q1},  [r1]!
        subs            r4,  r4,  #1
        vshl.s16        q0,  q0,  q15
        vld1.16         {q2,  q3},  [r1]!
        vshl.s16        q1,  q1,  q15
        vld1.16         {q8,  q9},  [r1]!
        vshl.s16        q2,  q2,  q15
        vld1.16         {q10, q11}, [r1], r2
        vshl.s16        q3,  q3,  q15
        vshl.s16        q8,  q8,  q15
        vshl.s16        q9,  q9,  q15
        vshl.s16        q10, q10, q15
        vshl.s16        q11, q11, q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vsub.i16        q2,  q2,  q14
        vsub.i16        q3,  q3,  q14
        vsub.i16        q8,  q8,  q14
        vst1.16         {q0,  q1},  [r0, :128]!
        vsub.i16        q9,  q9,  q14
        vst1.16         {q2,  q3},  [r0, :128]!
        vsub.i16        q10, q10, q14
        vst1.16         {q8,  q9},  [r0, :128]!
        vsub.i16        q11, q11, q14
        vst1.16         {q10, q11}, [r0, :128]!
        bgt             64b
        pop             {r4-r11,pc}
1280:
        sub             r2,  r2,  #224
128:
        vld1.16         {q0,  q1},  [r1]!
        subs            r4,  r4,  #1
        vshl.s16        q0,  q0,  q15
        vld1.16         {q2,  q3},  [r1]!
        vshl.s16        q1,  q1,  q15
        vld1.16         {q8,  q9},  [r1]!
        vshl.s16        q2,  q2,  q15
        vld1.16         {q10, q11}, [r1]!
        vshl.s16        q3,  q3,  q15
        vshl.s16        q8,  q8,  q15
        vshl.s16        q9,  q9,  q15
        vshl.s16        q10, q10, q15
        vshl.s16        q11, q11, q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vsub.i16        q2,  q2,  q14
        vsub.i16        q3,  q3,  q14
        vsub.i16        q8,  q8,  q14
        vst1.16         {q0,  q1},  [r0, :128]!
        vld1.16         {q0,  q1},  [r1]!
        vsub.i16        q9,  q9,  q14
        vsub.i16        q10, q10, q14
        vst1.16         {q2,  q3},  [r0, :128]!
        vld1.16         {q2,  q3},  [r1]!
        vsub.i16        q11, q11, q14
        vshl.s16        q0,  q0,  q15
        vst1.16         {q8,  q9},  [r0, :128]!
        vld1.16         {q8,  q9},  [r1]!
        vshl.s16        q1,  q1,  q15
        vshl.s16        q2,  q2,  q15
        vst1.16         {q10, q11}, [r0, :128]!
        vld1.16         {q10, q11}, [r1], r2
        vshl.s16        q3,  q3,  q15
        vshl.s16        q8,  q8,  q15
        vshl.s16        q9,  q9,  q15
        vshl.s16        q10, q10, q15
        vshl.s16        q11, q11, q15
        vsub.i16        q0,  q0,  q14
        vsub.i16        q1,  q1,  q14
        vsub.i16        q2,  q2,  q14
        vsub.i16        q3,  q3,  q14
        vsub.i16        q8,  q8,  q14
        vst1.16         {q0,  q1},  [r0, :128]!
        vsub.i16        q9,  q9,  q14
        vst1.16         {q2,  q3},  [r0, :128]!
        vsub.i16        q10, q10, q14
        vst1.16         {q8,  q9},  [r0, :128]!
        vsub.i16        q11, q11, q14
        vst1.16         {q10, q11}, [r0, :128]!
        bgt             128b
        pop             {r4-r11,pc}
endfunc

.macro load_slice s0, s1, strd, wd, d0, d1, d2, d3, d4, d5, d6
        vld1.\wd        {\d0[]}, [\s0], \strd
        vld1.\wd        {\d1[]}, [\s1], \strd
.ifnb \d2
        vld1.\wd        {\d2[]}, [\s0], \strd
        vld1.\wd        {\d3[]}, [\s1], \strd
.endif
.ifnb \d4
        vld1.\wd        {\d4[]}, [\s0], \strd
.endif
.ifnb \d5
        vld1.\wd        {\d5[]}, [\s1], \strd
.endif
.ifnb \d6
        vld1.\wd        {\d6[]}, [\s0], \strd
.endif
.endm
.macro load_reg s0, s1, strd, d0, d1, d2, d3, d4, d5, d6
        vld1.16         {\d0}, [\s0], \strd
        vld1.16         {\d1}, [\s1], \strd
.ifnb \d2
        vld1.16         {\d2}, [\s0], \strd
        vld1.16         {\d3}, [\s1], \strd
.endif
.ifnb \d4
        vld1.16         {\d4}, [\s0], \strd
.endif
.ifnb \d5
        vld1.16         {\d5}, [\s1], \strd
.endif
.ifnb \d6
        vld1.16         {\d6}, [\s0], \strd
.endif
.endm
.macro load_regpair s0, s1, strd, d0, d1, d2, d3, d4, d5
        vld1.16         {\d0, \d1}, [\s0], \strd
.ifnb \d2
        vld1.16         {\d2, \d3}, [\s1], \strd
.endif
.ifnb \d4
        vld1.16         {\d4, \d5}, [\s0], \strd
.endif
.endm
.macro load_32 s0, s1, strd, d0, d1, d2, d3, d4, d5, d6
        load_slice      \s0, \s1, \strd, 32, \d0, \d1, \d2, \d3, \d4, \d5, \d6
.endm
.macro load_16s16 s0, s1, strd, d0, d1, d2, d3, d4, d5
        load_regpair    \s0, \s1, \strd, \d0, \d1, \d2, \d3, \d4, \d5
.endm
.macro interleave_1_32 r0, r1, r2, r3, r4
        vext.8          \r0, \r0, \r1, #4
        vext.8          \r1, \r1, \r2, #4
.ifnb \r3
        vext.8          \r2, \r2, \r3, #4
        vext.8          \r3, \r3, \r4, #4
.endif
.endm
.macro vmin_u16 c, r0, r1, r2, r3
        vmin.u16        \r0, \r0, \c
.ifnb \r1
        vmin.u16        \r1, \r1, \c
.endif
.ifnb \r2
        vmin.u16        \r2, \r2, \c
        vmin.u16        \r3, \r3, \c
.endif
.endm
.macro vsub_i16 c, r0, r1, r2, r3
        vsub.i16        \r0, \r0, \c
.ifnb \r1
        vsub.i16        \r1, \r1, \c
.endif
.ifnb \r2
        vsub.i16        \r2, \r2, \c
        vsub.i16        \r3, \r3, \c
.endif
.endm
.macro vmull_vmlal_4 d, s0, s1, s2, s3
        vmull.s16       \d,  \s0, d0[0]
        vmlal.s16       \d,  \s1, d0[1]
        vmlal.s16       \d,  \s2, d0[2]
        vmlal.s16       \d,  \s3, d0[3]
.endm
.macro vmull_vmlal_8 d, s0, s1, s2, s3, s4, s5, s6, s7
        vmull.s16       \d,  \s0, d0[0]
        vmlal.s16       \d,  \s1, d0[1]
        vmlal.s16       \d,  \s2, d0[2]
        vmlal.s16       \d,  \s3, d0[3]
        vmlal.s16       \d,  \s4, d1[0]
        vmlal.s16       \d,  \s5, d1[1]
        vmlal.s16       \d,  \s6, d1[2]
        vmlal.s16       \d,  \s7, d1[3]
.endm
.macro vqrshrun_s32 shift, q0, d0, q1, d1, q2, d2, q3, d3
        vqrshrun.s32    \d0, \q0, #\shift
.ifnb \q1
        vqrshrun.s32    \d1, \q1, #\shift
.endif
.ifnb \q2
        vqrshrun.s32    \d2, \q2, #\shift
        vqrshrun.s32    \d3, \q3, #\shift
.endif
.endm
.macro vmovn_i32 q0, d0, q1, d1, q2, d2, q3, d3
        vmovn.i32       \d0, \q0
.ifnb \q1
        vmovn.i32       \d1, \q1
.endif
.ifnb \q2
        vmovn.i32       \d2, \q2
        vmovn.i32       \d3, \q3
.endif
.endm
.macro vrshl_s32 shift, r0, r1, r2, r3
        vrshl.s32       \r0, \r0, \shift
        vrshl.s32       \r1, \r1, \shift
.ifnb \r2
        vrshl.s32       \r2, \r2, \shift
        vrshl.s32       \r3, \r3, \shift
.endif
.endm
.macro vst1_32 strd, r0, r1
        vst1.32         {\r0[0]}, [r0, :32], \strd
        vst1.32         {\r0[1]}, [r9, :32], \strd
.ifnb \r1
        vst1.32         {\r1[0]}, [r0, :32], \strd
        vst1.32         {\r1[1]}, [r9, :32], \strd
.endif
.endm
.macro vst1_reg strd, align, r0, r1, r2, r3, r4, r5, r6, r7
        vst1.16         {\r0}, [r0, \align], \strd
        vst1.16         {\r1}, [r9, \align], \strd
.ifnb \r2
        vst1.16         {\r2}, [r0, \align], \strd
        vst1.16         {\r3}, [r9, \align], \strd
.endif
.ifnb \r4
        vst1.16         {\r4}, [r0, \align], \strd
        vst1.16         {\r5}, [r9, \align], \strd
        vst1.16         {\r6}, [r0, \align], \strd
        vst1.16         {\r7}, [r9, \align], \strd
.endif
.endm
.macro finalize type, q0, q1, d0, d1, q2, q3, d2, d3
.ifc \type, put
        vqrshrun_s32    6,   \q0, \d0, \q1, \d1, \q2, \d2, \q3, \d3
        vmin_u16        q15, \q0, \q1
.else
        vrshl_s32       q14, \q0, \q1, \q2, \q3 // -(6-intermediate_bits)
        vmovn_i32       \q0, \d0, \q1, \d1, \q2, \d2, \q3, \d3
        vsub_i16        q15, \q0, \q1           // PREP_BIAS
.endif
.endm
.macro shift_store_4 type, strd, q0, q1, d0, d1, q2, q3, d2, d3
        finalize        \type, \q0, \q1, \d0, \d1, \q2, \q3, \d2, \d3
        vst1_reg        \strd, :64, \d0, \d1, \d2, \d3
.endm
.macro shift_store_8 type, strd, q0, q1, d0, d1, q2, q3, d2, d3
        finalize        \type, \q0, \q1, \d0, \d1, \q2, \q3, \d2, \d3
        vst1_reg        \strd, :128, \q0, \q1
.endm
.macro shift_store_16 type, strd, q0, q1, d0, d1, q2, q3, d2, d3
        finalize        \type, \q0, \q1, \d0, \d1, \q2, \q3, \d2, \d3
        vst1.16         {\q0, \q1}, [r0, :128], \strd
.endm

.macro make_8tap_fn op, type, type_h, type_v
function \op\()_8tap_\type\()_16bpc_neon, export=1
        push            {r4-r11,lr}
        movw            r9,  \type_h
        movw            r10, \type_v
        b               \op\()_8tap_neon
endfunc
.endm

// No spaces in these expressions, due to gas-preprocessor.
#define REGULAR ((0*15<<7)|3*15)
#define SMOOTH  ((1*15<<7)|4*15)
#define SHARP   ((2*15<<7)|3*15)

.macro filter_fn type, dst, d_strd, src, s_strd, w, h, mx, my, bdmax, ds2, sr2
make_8tap_fn \type, regular,        REGULAR, REGULAR
make_8tap_fn \type, regular_smooth, REGULAR, SMOOTH
make_8tap_fn \type, regular_sharp,  REGULAR, SHARP
make_8tap_fn \type, smooth,         SMOOTH,  SMOOTH
make_8tap_fn \type, smooth_regular, SMOOTH,  REGULAR
make_8tap_fn \type, smooth_sharp,   SMOOTH,  SHARP
make_8tap_fn \type, sharp,          SHARP,   SHARP
make_8tap_fn \type, sharp_regular,  SHARP,   REGULAR
make_8tap_fn \type, sharp_smooth,   SHARP,   SMOOTH

function \type\()_8tap_neon
        ldrd            r4,  r5,  [sp, #36]
        ldrd            r6,  r7,  [sp, #44]
.ifc \bdmax, r8
        ldr             r8,  [sp, #52]
.endif
        movw            r11, #0x4081  // (1 << 14) | (1 << 7) | (1 << 0)
        mul             \mx, \mx, r11
        mul             \my, \my, r11
        add             \mx, \mx, r9  // mx, 8tap_h, 4tap_h
        add             \my, \my, r10 // my, 8tap_v, 4tap_v

.ifc \type, prep
        lsl             \d_strd, \w, #1
.endif

        vdup.16         q15, \bdmax            // bitdepth_max
        clz             \bdmax,  \bdmax
        clz             r9,  \w
        sub             \bdmax,  \bdmax,  #18  // intermediate_bits = clz(bitdepth_max) - 18
        tst             \mx, #(0x7f << 14)
        sub             r9,  r9,  #24
        add             lr,  \bdmax, #6        // 6 + intermediate_bits
        rsb             r12, \bdmax, #6        // 6 - intermediate_bits
        movrel          r11, X(mc_subpel_filters), -8
        bne             L(\type\()_8tap_h)
        tst             \my, #(0x7f << 14)
        bne             L(\type\()_8tap_v)
        b               \type\()_neon

L(\type\()_8tap_h):
        cmp             \w,  #4
        ubfx            r10, \mx, #7,  #7
        and             \mx, \mx, #0x7f
        it              gt
        movgt           \mx, r10
        tst             \my, #(0x7f << 14)
        add             \mx, r11, \mx, lsl #3
        bne             L(\type\()_8tap_hv)

        adr             r10, L(\type\()_8tap_h_tbl)
        vdup.32         q14, r12           // 6 - intermediate_bits
        ldr             r9,  [r10, r9, lsl #2]
        vneg.s32        q14, q14           // -(6-intermediate_bits)
.ifc \type, put
        vdup.16         q13, \bdmax        // intermediate_bits
.else
        vmov.i16        q13, #PREP_BIAS
.endif
        add             r10, r10, r9
.ifc \type, put
        vneg.s16        q13, q13           // -intermediate_bits
.endif
        bx              r10

        .align 2
L(\type\()_8tap_h_tbl):
        .word 1280f - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_8tap_h_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_8tap_h_tbl) + CONFIG_THUMB

20:     // 2xN h
.ifc \type, put
        add             \mx, \mx, #2
        vld1.32         {d0[]}, [\mx]
        sub             \src,  \src,  #2
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
        vmovl.s8        q0,  d0
2:
        vld1.16         {q2}, [\src], \s_strd
        vld1.16         {q3}, [\sr2], \s_strd
        vext.8          d5,  d4,  d5,  #2
        vext.8          d7,  d6,  d7,  #2
        subs            \h,  \h,  #2
        vtrn.32         d4,  d6
        vtrn.32         d5,  d7
        vmull.s16       q1,  d4,  d0[0]
        vmlal.s16       q1,  d5,  d0[1]
        vmlal.s16       q1,  d6,  d0[2]
        vmlal.s16       q1,  d7,  d0[3]
        vrshl.s32       q1,  q1,  q14 // -(6-intermediate_bits)
        vqmovun.s32     d2,  q1
        vrshl.s16       d2,  d2,  d26 // -intermediate_bits
        vmin.u16        d2,  d2,  d30
        vst1.32         {d2[0]}, [\dst, :32], \d_strd
        vst1.32         {d2[1]}, [\ds2, :32], \d_strd
        bgt             2b
        pop             {r4-r11,pc}
.endif

40:     // 4xN h
        add             \mx, \mx, #2
        vld1.32         {d0[]}, [\mx]
        sub             \src,  \src,  #2
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
        vmovl.s8        q0,  d0
4:
        vld1.16         {q8},  [\src], \s_strd
        vld1.16         {q11}, [\sr2], \s_strd
        vext.8          d18, d16, d17, #2
        vext.8          d19, d16, d17, #4
        vext.8          d20, d16, d17, #6
        vext.8          d24, d22, d23, #2
        vext.8          d25, d22, d23, #4
        vext.8          d21, d22, d23, #6
        subs            \h,  \h,  #2
        vmull.s16       q2,  d16, d0[0]
        vmlal.s16       q2,  d18, d0[1]
        vmlal.s16       q2,  d19, d0[2]
        vmlal.s16       q2,  d20, d0[3]
        vmull.s16       q3,  d22, d0[0]
        vmlal.s16       q3,  d24, d0[1]
        vmlal.s16       q3,  d25, d0[2]
        vmlal.s16       q3,  d21, d0[3]
        vrshl.s32       q2,  q2,  q14 // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
.ifc \type, put
        vqmovun.s32     d4,  q2
        vqmovun.s32     d5,  q3
        vrshl.s16       q2,  q2,  q13 // -intermediate_bits
        vmin.u16        q2,  q2,  q15
.else
        vmovn.s32       d4,  q2
        vmovn.s32       d5,  q3
        vsub.i16        q2,  q2,  q13 // PREP_BIAS
.endif
        vst1.16         {d4}, [\dst, :64], \d_strd
        vst1.16         {d5}, [\ds2, :64], \d_strd
        bgt             4b
        pop             {r4-r11,pc}

80:
160:
320:
640:
1280:   // 8xN, 16xN, 32xN, ... h
        vpush           {q4-q5}
        vld1.8          {d0}, [\mx, :64]
        sub             \src,  \src,  #6
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1
        vmovl.s8        q0,  d0

        sub             \s_strd,  \s_strd,  \w, lsl #1
        sub             \s_strd,  \s_strd,  #16
.ifc \type, put
        lsl             \d_strd,  \d_strd,  #1
        sub             \d_strd,  \d_strd,  \w, lsl #1
.endif
81:
        vld1.16         {q8,  q9},  [\src]!
        vld1.16         {q10, q11}, [\sr2]!
        mov             \mx, \w

8:
        vmull.s16       q1,  d16, d0[0]
        vmull.s16       q2,  d17, d0[0]
        vmull.s16       q3,  d20, d0[0]
        vmull.s16       q4,  d21, d0[0]
.irpc i, 1234567
        vext.8          q12, q8,  q9,  #(2*\i)
        vext.8          q5,  q10, q11, #(2*\i)
.if \i < 4
        vmlal.s16       q1,  d24, d0[\i]
        vmlal.s16       q2,  d25, d0[\i]
        vmlal.s16       q3,  d10, d0[\i]
        vmlal.s16       q4,  d11, d0[\i]
.else
        vmlal.s16       q1,  d24, d1[\i-4]
        vmlal.s16       q2,  d25, d1[\i-4]
        vmlal.s16       q3,  d10, d1[\i-4]
        vmlal.s16       q4,  d11, d1[\i-4]
.endif
.endr
        subs            \mx, \mx, #8
        vrshl.s32       q1,  q1,  q14 // -(6-intermediate_bits)
        vrshl.s32       q2,  q2,  q14 // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
        vrshl.s32       q4,  q4,  q14 // -(6-intermediate_bits)
.ifc \type, put
        vqmovun.s32     d2,  q1
        vqmovun.s32     d3,  q2
        vqmovun.s32     d4,  q3
        vqmovun.s32     d5,  q4
        vrshl.s16       q1,  q1,  q13 // -intermediate_bits
        vrshl.s16       q2,  q2,  q13 // -intermediate_bits
        vmin.u16        q1,  q1,  q15
        vmin.u16        q2,  q2,  q15
.else
        vmovn.s32       d2,  q1
        vmovn.s32       d3,  q2
        vmovn.s32       d4,  q3
        vmovn.s32       d5,  q4
        vsub.i16        q1,  q1,  q13 // PREP_BIAS
        vsub.i16        q2,  q2,  q13 // PREP_BIAS
.endif
        vst1.16         {q1}, [\dst, :128]!
        vst1.16         {q2}, [\ds2, :128]!
        ble             9f

        vmov            q8,  q9
        vmov            q10, q11
        vld1.16         {q9},  [\src]!
        vld1.16         {q11}, [\sr2]!
        b               8b

9:
        add             \dst,  \dst,  \d_strd
        add             \ds2,  \ds2,  \d_strd
        add             \src,  \src,  \s_strd
        add             \sr2,  \sr2,  \s_strd

        subs            \h,  \h,  #2
        bgt             81b
        vpop            {q4-q5}
        pop             {r4-r11,pc}


L(\type\()_8tap_v):
        cmp             \h,  #4
        ubfx            r10, \my, #7,  #7
        and             \my, \my, #0x7f
        it              gt
        movgt           \my, r10
        add             \my, r11, \my, lsl #3

.ifc \type, prep
        vdup.32         q14, r12        // 6 - intermediate_bits
        vmov.i16        q15, #PREP_BIAS
.endif
        adr             r10, L(\type\()_8tap_v_tbl)
        ldr             r9,  [r10, r9, lsl #2]
.ifc \type, prep
        vneg.s32        q14, q14        // -(6-intermediate_bits)
.endif
        add             r10, r10, r9
        bx              r10

        .align 2
L(\type\()_8tap_v_tbl):
        .word 1280f - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_8tap_v_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_8tap_v_tbl) + CONFIG_THUMB

20:     // 2xN v
.ifc \type, put
        bgt             28f

        cmp             \h,  #2
        add             \my, \my, #2
        vld1.32         {d0[]}, [\my]
        sub             \src,  \src,  \s_strd
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1
        lsl             \d_strd,  \d_strd,  #1
        vmovl.s8        q0,  d0

        // 2x2 v
        load_32         \src, \sr2, \s_strd, d1, d2, d3, d4, d5
        interleave_1_32 d1,  d2,  d3,  d4,  d5
        bgt             24f
        vmull_vmlal_4   q8,  d1,  d2,  d3,  d4
        vqrshrun_s32    6,   q8,  d16
        vmin_u16        d30, d16
        vst1_32         \d_strd,  d16
        pop             {r4-r11,pc}

24:     // 2x4 v
        load_32         \sr2, \src, \s_strd, d6, d7
        interleave_1_32 d5,  d6,  d7
        vmull_vmlal_4   q8,  d1,  d2,  d3,  d4
        vmull_vmlal_4   q9,  d3,  d4,  d5,  d6
        vqrshrun_s32    6,   q8,  d16, q9,  d17
        vmin_u16        q15, q8
        vst1_32         \d_strd,  d16, d17
        pop             {r4-r11,pc}

28:     // 2x8, 2x16 v
        vld1.8          {d0}, [\my, :64]
        sub             \sr2,  \src,  \s_strd, lsl #1
        add             \ds2,  \dst,  \d_strd
        sub             \src,  \sr2,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
        vmovl.s8        q0,  d0

        load_32         \src, \sr2, \s_strd, d2, d3, d4, d5, d6, d7, d16
        interleave_1_32 d2,  d3,  d4,  d5,  d6
        interleave_1_32 d6,  d7,  d16
216:
        subs            \h,  \h,  #8
        load_32         \sr2, \src, \s_strd, d17, d18, d19, d20
        load_32         \sr2, \src, \s_strd, d21, d22, d23, d24
        interleave_1_32 d16, d17, d18, d19, d20
        interleave_1_32 d20, d21, d22, d23, d24
        vmull_vmlal_8   q13, d2,  d3,  d4,  d5,  d6,  d7,  d16, d17
        vmull_vmlal_8   q1,  d4,  d5,  d6,  d7,  d16, d17, d18, d19
        vmull_vmlal_8   q2,  d6,  d7,  d16, d17, d18, d19, d20, d21
        vmull_vmlal_8   q3,  d16, d17, d18, d19, d20, d21, d22, d23
        vqrshrun_s32    6,   q13, d26, q1,  d27, q2,  d2,  q3,  d3
        vmin_u16        q15, q13, q1
        vst1_32         \d_strd,  d26, d27
        vst1_32         \d_strd,  d2,  d3
        ble             0f
        vmov            q1,  q9
        vmov            q2,  q10
        vmov            q3,  q11
        vmov            d16, d24
        b               216b
0:
        pop             {r4-r11,pc}
.endif

40:
        bgt             480f

        // 4x2, 4x4 v
        cmp             \h,  #2
        add             \my, \my, #2
        vld1.32         {d0[]}, [\my]
        sub             \src, \src, \s_strd
        add             \ds2, \dst, \d_strd
        add             \sr2, \src, \s_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0

        load_reg        \src, \sr2, \s_strd, d1, d2, d3, d4, d5
        vmull_vmlal_4   q8,  d1,  d2,  d3,  d4
        vmull_vmlal_4   q9,  d2,  d3,  d4,  d5
        shift_store_4   \type, \d_strd, q8, q9, d16, d17
        ble             0f
        load_reg        \sr2, \src, \s_strd, d6, d7
        vmull_vmlal_4   q8,  d3,  d4,  d5,  d6
        vmull_vmlal_4   q9,  d4,  d5,  d6,  d7
        shift_store_4   \type, \d_strd, q8, q9, d16, d17
0:
        pop             {r4-r11,pc}

480:    // 4x8, 4x16 v
        vld1.8          {d0}, [\my, :64]
        sub             \sr2, \src, \s_strd, lsl #1
        add             \ds2, \dst, \d_strd
        sub             \src, \sr2, \s_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0

        load_reg        \src, \sr2, \s_strd, d16, d17, d18, d19, d20, d21, d22

48:
        subs            \h,  \h,  #4
        load_reg        \sr2, \src, \s_strd, d23, d24, d25, d26
        vmull_vmlal_8   q1,  d16, d17, d18, d19, d20, d21, d22, d23
        vmull_vmlal_8   q2,  d17, d18, d19, d20, d21, d22, d23, d24
        vmull_vmlal_8   q3,  d18, d19, d20, d21, d22, d23, d24, d25
        vmull_vmlal_8   q8,  d19, d20, d21, d22, d23, d24, d25, d26
        shift_store_4   \type, \d_strd, q1, q2, d2, d3, q3, q8, d4, d5
        ble             0f
        vmov            q8,  q10
        vmov            q9,  q11
        vmov            q10, q12
        vmov            d22, d26
        b               48b
0:
        pop             {r4-r11,pc}

80:
        bgt             880f

        // 8x2, 8x4 v
        cmp             \h,  #2
        add             \my, \my, #2
        vld1.32         {d0[]}, [\my]
        sub             \src, \src, \s_strd
        add             \ds2, \dst, \d_strd
        add             \sr2, \src, \s_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0

        load_reg        \src, \sr2, \s_strd, q1, q2, q3, q8, q9
        vmull_vmlal_4   q10, d2,  d4,  d6,  d16
        vmull_vmlal_4   q11, d3,  d5,  d7,  d17
        vmull_vmlal_4   q12, d4,  d6,  d16, d18
        vmull_vmlal_4   q13, d5,  d7,  d17, d19
        shift_store_8   \type, \d_strd, q10, q11, d20, d21, q12, q13, d22, d23
        ble             0f
        load_reg        \sr2, \src, \s_strd, q10, q11
        vmull_vmlal_4   q1,  d6,  d16, d18, d20
        vmull_vmlal_4   q2,  d7,  d17, d19, d21
        vmull_vmlal_4   q12, d16, d18, d20, d22
        vmull_vmlal_4   q13, d17, d19, d21, d23
        shift_store_8   \type, \d_strd, q1, q2, d2, d3, q12, q13, d4, d5
0:
        pop             {r4-r11,pc}

880:    // 8x6, 8x8, 8x16, 8x32 v
1680:   // 16x8, 16x16, ...
320:    // 32x8, 32x16, ...
640:
1280:
        vpush           {q4-q7}
        vld1.8          {d0}, [\my, :64]
        sub             \src, \src, \s_strd
        sub             \src, \src, \s_strd, lsl #1
        vmovl.s8        q0,  d0
        mov             \my, \h
168:
        add             \ds2, \dst, \d_strd
        add             \sr2, \src, \s_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1

        load_reg        \src, \sr2, \s_strd, q5, q6, q7, q8, q9, q10, q11

88:
        subs            \h,  \h,  #2
        load_reg        \sr2, \src, \s_strd, q12, q13
        vmull_vmlal_8   q1,  d10, d12, d14, d16, d18, d20, d22, d24
        vmull_vmlal_8   q2,  d11, d13, d15, d17, d19, d21, d23, d25
        vmull_vmlal_8   q3,  d12, d14, d16, d18, d20, d22, d24, d26
        vmull_vmlal_8   q4,  d13, d15, d17, d19, d21, d23, d25, d27
        shift_store_8   \type, \d_strd, q1, q2,  d2,  d3,  q3,  q4,  d4,  d5
        ble             9f
        subs            \h,  \h,  #2
        load_reg        \sr2, \src, \s_strd, q1,  q2
        vmull_vmlal_8   q3,  d14, d16, d18, d20, d22, d24, d26, d2
        vmull_vmlal_8   q4,  d15, d17, d19, d21, d23, d25, d27, d3
        vmull_vmlal_8   q5,  d16, d18, d20, d22, d24, d26, d2,  d4
        vmull_vmlal_8   q6,  d17, d19, d21, d23, d25, d27, d3,  d5
        shift_store_8   \type, \d_strd, q3, q4,  d6,  d7,  q5,  q6,  d8,  d9
        ble             9f
        vmov            q5,  q9
        vmov            q6,  q10
        vmov            q7,  q11
        vmov            q8,  q12
        vmov            q9,  q13
        vmov            q10, q1
        vmov            q11, q2
        b               88b
9:
        subs            \w,  \w,  #8
        ble             0f
        asr             \s_strd, \s_strd, #1
        asr             \d_strd, \d_strd, #1
        mls             \src, \s_strd, \my, \src
        mls             \dst, \d_strd, \my, \dst
        sub             \src, \src, \s_strd, lsl #3
        mov             \h,  \my
        add             \src, \src, #16
        add             \dst, \dst, #16
        b               168b
0:
        vpop            {q4-q7}
        pop             {r4-r11,pc}

160:
        bgt             1680b

        // 16x2, 16x4 v
        vpush           {q6-q7}
        add             \my, \my, #2
        vld1.32         {d0[]}, [\my]
        sub             \src, \src, \s_strd
        vmovl.s8        q0,  d0

        load_16s16      \src, \src, \s_strd, q6,  q7,  q8,  q9, q10, q11
16:
        load_16s16      \src, \src, \s_strd, q12, q13
        subs            \h,  \h,  #1
        vmull_vmlal_4   q1,  d12, d16, d20, d24
        vmull_vmlal_4   q2,  d13, d17, d21, d25
        vmull_vmlal_4   q3,  d14, d18, d22, d26
        vmull_vmlal_4   q6,  d15, d19, d23, d27
        shift_store_16  \type, \d_strd, q1, q2, d2, d3, q3, q6, d4, d5
        ble             0f
        vmov            q6,  q8
        vmov            q7,  q9
        vmov            q8,  q10
        vmov            q9,  q11
        vmov            q10, q12
        vmov            q11, q13
        b               16b
0:
        vpop            {q6-q7}
        pop             {r4-r11,pc}


L(\type\()_8tap_hv):
        cmp             \h,  #4
        ubfx            r10, \my, #7,  #7
        and             \my, \my, #0x7f
        it              gt
        movgt           \my, r10
4:
        add             \my, r11, \my, lsl #3

        adr             r10, L(\type\()_8tap_hv_tbl)
        neg             r12, r12           // -(6-intermediate_bits)
        ldr             r9,  [r10, r9, lsl #2]
        vdup.32         q14, r12           // -(6-intermediate_bits)
.ifc \type, put
        neg             r8,  lr            // -(6+intermeidate_bits)
.else
        vmov.i16        q13, #PREP_BIAS
.endif
        add             r10, r10, r9
.ifc \type, put
        vdup.32         q13, r8            // -(6+intermediate_bits)
.endif
        bx              r10

        .align 2
L(\type\()_8tap_hv_tbl):
        .word 1280f - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_8tap_hv_tbl) + CONFIG_THUMB

20:
.ifc \type, put
        add             \mx, \mx, #2
        vld1.32         {d0[]}, [\mx]
        bgt             280f
        add             \my, \my, #2
        vld1.32         {d2[]}, [\my]

        // 2x2, 2x4 hv
        sub             \sr2, \src, #2
        sub             \src, \sr2, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2

        vld1.16         {q11}, [\src], \s_strd
        vext.8          d24, d22, d23, #2
        vmull.s16       q11, d22, d0
        vmull.s16       q12, d24, d0
        vpadd.s32       d22, d22, d23
        vpadd.s32       d23, d24, d25
        vpadd.s32       d22, d22, d23
        vrshl.s32       d16, d22, d28 // -(6-intermediate_bits)
        vmovn.i32       d16, q8
        bl              L(\type\()_8tap_filter_2)

        vext.8          d16, d16, d16, #4
        vext.8          d16, d16, d24, #4
        vmov            d17, d24

2:
        bl              L(\type\()_8tap_filter_2)

        vext.8          d18, d17, d24, #4
        vmull.s16       q2,  d16, d2[0]
        vmlal.s16       q2,  d17, d2[1]
        vmlal.s16       q2,  d18, d2[2]
        vmlal.s16       q2,  d24, d2[3]

        vrshl.s32       q2,  q2,  q13 // -(6+intermediate_bits)
        vqmovun.s32     d4,  q2
        vmin.u16        d4,  d4,  d30
        subs            \h,  \h,  #2
        vst1.32         {d4[0]}, [\dst, :32], \d_strd
        vst1.32         {d4[1]}, [\ds2, :32], \d_strd
        ble             0f
        vmov            d16, d18
        vmov            d17, d24
        b               2b

280:    // 2x8, 2x16, 2x32 hv
        vld1.8          {d2},  [\my, :64]
        sub             \src, \src, #2
        sub             \sr2, \src, \s_strd, lsl #1
        sub             \src, \sr2, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2

        vld1.16         {q11}, [\src], \s_strd
        vext.8          d24, d22, d23, #2
        vmull.s16       q11, d22, d0
        vmull.s16       q12, d24, d0
        vpadd.s32       d22, d22, d23
        vpadd.s32       d23, d24, d25
        vpadd.s32       d22, d22, d23
        vrshl.s32       d16, d22, d28 // -(6-intermediate_bits)
        vmovn.i32       d16, q8

        bl              L(\type\()_8tap_filter_2)

        vext.8          d16, d16, d16, #4
        vext.8          d16, d16, d24, #4
        vmov            d17, d24
        bl              L(\type\()_8tap_filter_2)
        vext.8          d18, d17, d24, #4
        vmov            d19, d24
        bl              L(\type\()_8tap_filter_2)
        vext.8          d20, d19, d24, #4
        vmov            d21, d24

28:
        bl              L(\type\()_8tap_filter_2)
        vext.8          d22, d21, d24, #4
        vmull.s16       q3,  d16, d2[0]
        vmlal.s16       q3,  d17, d2[1]
        vmlal.s16       q3,  d18, d2[2]
        vmlal.s16       q3,  d19, d2[3]
        vmlal.s16       q3,  d20, d3[0]
        vmlal.s16       q3,  d21, d3[1]
        vmlal.s16       q3,  d22, d3[2]
        vmlal.s16       q3,  d24, d3[3]

        vrshl.s32       q3,  q3,  q13 // -(6+intermediate_bits)
        vqmovun.s32     d6,  q3
        vmin.u16        d6,  d6,  d30
        subs            \h,  \h,  #2
        vst1.32         {d6[0]}, [\dst, :32], \d_strd
        vst1.32         {d6[1]}, [\ds2, :32], \d_strd
        ble             0f
        vmov            q8,  q9
        vmov            q9,  q10
        vmov            d20, d22
        vmov            d21, d24
        b               28b
0:
        pop             {r4-r11,pc}

L(\type\()_8tap_filter_2):
        vld1.16         {q11}, [\sr2], \s_strd
        vld1.16         {q12}, [\src], \s_strd
        vext.8          d23, d22, d23, #2
        vext.8          d25, d24, d25, #2
        vtrn.32         q11, q12
        vmull.s16       q3,  d22, d0[0]
        vmlal.s16       q3,  d23, d0[1]
        vmlal.s16       q3,  d24, d0[2]
        vmlal.s16       q3,  d25, d0[3]
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
        vmovn.i32       d24, q3
        bx              lr
.endif

40:
        add             \mx, \mx, #2
        vld1.32         {d0[]}, [\mx]
        bgt             480f
        add             \my, \my, #2
        vld1.32         {d2[]}, [\my]
        sub             \sr2, \src, #2
        sub             \src, \sr2, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2

        // 4x2, 4x4 hv
        vld1.16         {q11}, [\src], \s_strd
        vext.8          d24, d22, d23, #2
        vext.8          d25, d22, d23, #4
        vext.8          d23, d22, d23, #6
        vmull.s16       q10, d22, d0[0]
        vmlal.s16       q10, d24, d0[1]
        vmlal.s16       q10, d25, d0[2]
        vmlal.s16       q10, d23, d0[3]
        vrshl.s32       q10, q10, q14 // -(6-intermediate_bits)
        vmovn.i32       d17, q10

        bl              L(\type\()_8tap_filter_4)
        vmov            q9,  q12

4:
        bl              L(\type\()_8tap_filter_4)
        vmull.s16       q2,  d17, d2[0]
        vmlal.s16       q2,  d18, d2[1]
        vmlal.s16       q2,  d19, d2[2]
        vmlal.s16       q2,  d24, d2[3]
        vmull.s16       q3,  d18, d2[0]
        vmlal.s16       q3,  d19, d2[1]
        vmlal.s16       q3,  d24, d2[2]
        vmlal.s16       q3,  d25, d2[3]
.ifc \type, put
        vrshl.s32       q2,  q2,  q13 // -(6+intermediate_bits)
        vrshl.s32       q3,  q3,  q13 // -(6+intermediate_bits)
        vqmovun.s32     d4,  q2
        vqmovun.s32     d5,  q3
        vmin.u16        q2,  q2,  q15
.else
        vrshrn.i32      d4,  q2,  #6
        vrshrn.i32      d5,  q3,  #6
        vsub.i16        q2,  q2,  q13 // PREP_BIAS
.endif
        subs            \h,  \h,  #2

        vst1.16         {d4}, [\dst, :64], \d_strd
        vst1.16         {d5}, [\ds2, :64], \d_strd
        ble             0f
        vmov            d17, d19
        vmov            q9,  q12
        b               4b
0:
        pop             {r4-r11,pc}

480:    // 4x8, 4x16, 4x32 hv
        vpush           {d13-d15}
        vld1.8          {d2},  [\my, :64]
        sub             \src, \src, #2
        sub             \sr2, \src, \s_strd, lsl #1
        sub             \src, \sr2, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2

        vld1.16         {q11}, [\src], \s_strd
        vext.8          d24, d22, d23, #2
        vext.8          d25, d22, d23, #4
        vext.8          d23, d22, d23, #6
        vmull.s16       q10, d22, d0[0]
        vmlal.s16       q10, d24, d0[1]
        vmlal.s16       q10, d25, d0[2]
        vmlal.s16       q10, d23, d0[3]
        vrshl.s32       q10, q10, q14 // -(6-intermediate_bits)
        vmovn.i32       d13, q10

        bl              L(\type\()_8tap_filter_4)
        vmov            q7,  q12
        bl              L(\type\()_8tap_filter_4)
        vmov            q8,  q12
        bl              L(\type\()_8tap_filter_4)
        vmov            q9,  q12

48:
        bl              L(\type\()_8tap_filter_4)
        vmull.s16       q2,  d13, d2[0]
        vmlal.s16       q2,  d14, d2[1]
        vmlal.s16       q2,  d15, d2[2]
        vmlal.s16       q2,  d16, d2[3]
        vmlal.s16       q2,  d17, d3[0]
        vmlal.s16       q2,  d18, d3[1]
        vmlal.s16       q2,  d19, d3[2]
        vmlal.s16       q2,  d24, d3[3]
        vmull.s16       q3,  d14, d2[0]
        vmlal.s16       q3,  d15, d2[1]
        vmlal.s16       q3,  d16, d2[2]
        vmlal.s16       q3,  d17, d2[3]
        vmlal.s16       q3,  d18, d3[0]
        vmlal.s16       q3,  d19, d3[1]
        vmlal.s16       q3,  d24, d3[2]
        vmlal.s16       q3,  d25, d3[3]
.ifc \type, put
        vrshl.s32       q2,  q2,  q13 // -(6+intermediate_bits)
        vrshl.s32       q3,  q3,  q13 // -(6+intermediate_bits)
        vqmovun.s32     d4,  q2
        vqmovun.s32     d5,  q3
        vmin.u16        q2,  q2,  q15
.else
        vrshrn.i32      d4,  q2,  #6
        vrshrn.i32      d5,  q3,  #6
        vsub.i16        q2,  q2,  q13 // PREP_BIAS
.endif
        subs            \h,  \h,  #2
        vst1.16         {d4}, [\dst, :64], \d_strd
        vst1.16         {d5}, [\ds2, :64], \d_strd
        ble             0f
        vmov            d13, d15
        vmov            q7,  q8
        vmov            q8,  q9
        vmov            q9,  q12
        b               48b
0:
        vpop            {d13-d15}
        pop             {r4-r11,pc}

L(\type\()_8tap_filter_4):
        vld1.16         {q10}, [\sr2], \s_strd
        vld1.16         {q11}, [\src], \s_strd
        vext.8          d24, d20, d21, #2
        vext.8          d25, d20, d21, #4
        vext.8          d21, d20, d21, #6
        vmull.s16       q3,  d20, d0[0]
        vmlal.s16       q3,  d24, d0[1]
        vmlal.s16       q3,  d25, d0[2]
        vmlal.s16       q3,  d21, d0[3]
        vext.8          d24, d22, d23, #2
        vext.8          d25, d22, d23, #4
        vext.8          d23, d22, d23, #6
        vmull.s16       q10, d22, d0[0]
        vmlal.s16       q10, d24, d0[1]
        vmlal.s16       q10, d25, d0[2]
        vmlal.s16       q10, d23, d0[3]
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
        vrshl.s32       q10, q10, q14 // -(6-intermediate_bits)
        vmovn.i32       d24, q3
        vmovn.i32       d25, q10
        bx              lr

80:
160:
320:
        bgt             880f
        add             \my, \my, #2
        vld1.8          {d0},  [\mx, :64]
        vld1.32         {d2[]}, [\my]
        sub             \src,  \src,  #6
        sub             \src,  \src,  \s_strd
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2
        mov             \my, \h

164:    // 8x2, 8x4, 16x2, 16x4, 32x2, 32x4 hv
        add             \ds2, \dst, \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd, \d_strd, #1
        lsl             \s_strd, \s_strd, #1

        vld1.16         {q11, q12}, [\src], \s_strd
        vmull.s16       q2,  d22, d0[0]
        vmull.s16       q3,  d23, d0[0]
        vdup.32         q14, r12      // -(6-intermediate_bits)
.irpc i, 1234567
        vext.8          q10, q11, q12, #(2*\i)
.if \i < 4
        vmlal.s16       q2,  d20, d0[\i]
        vmlal.s16       q3,  d21, d0[\i]
.else
        vmlal.s16       q2,  d20, d1[\i - 4]
        vmlal.s16       q3,  d21, d1[\i - 4]
.endif
.endr
        vrshl.s32       q2,  q2,  q14 // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
        vmovn.i32       d16, q2
        vmovn.i32       d17, q3

        bl              L(\type\()_8tap_filter_8)
        vmov            q9,  q11
        vmov            q10, q12

8:
        bl              L(\type\()_8tap_filter_8)
        vmull.s16       q2,  d16, d2[0]
        vmull.s16       q3,  d17, d2[0]
        vmull.s16       q13, d18, d2[0]
        vmull.s16       q14, d19, d2[0]
.ifc \type, put
        vdup.32         q8,  r8      // -(6+intermediate_bits)
.endif
        vmlal.s16       q2,  d18, d2[1]
        vmlal.s16       q3,  d19, d2[1]
        vmlal.s16       q13, d20, d2[1]
        vmlal.s16       q14, d21, d2[1]
        vmlal.s16       q2,  d20, d2[2]
        vmlal.s16       q3,  d21, d2[2]
        vmlal.s16       q13, d22, d2[2]
        vmlal.s16       q14, d23, d2[2]
        vmlal.s16       q2,  d22, d2[3]
        vmlal.s16       q3,  d23, d2[3]
        vmlal.s16       q13, d24, d2[3]
        vmlal.s16       q14, d25, d2[3]
.ifc \type, put
        vdup.16         q9,  \bdmax  // bitdepth_max
        vrshl.s32       q2,  q2,  q8 // -(6+intermediate_bits)
        vrshl.s32       q3,  q3,  q8 // -(6+intermediate_bits)
        vrshl.s32       q13, q13, q8 // -(6+intermediate_bits)
        vrshl.s32       q14, q14, q8 // -(6+intermediate_bits)
        vqmovun.s32     d4,  q2
        vqmovun.s32     d5,  q3
        vqmovun.s32     d6,  q13
        vqmovun.s32     d7,  q14
        vmin.u16        q2,  q2,  q15
        vmin.u16        q3,  q3,  q15
.else
        vmov.i16        q9,  #PREP_BIAS
        vrshrn.i32      d4,  q2,  #6
        vrshrn.i32      d5,  q3,  #6
        vrshrn.i32      d6,  q13, #6
        vrshrn.i32      d7,  q14, #6
        vsub.i16        q2,  q2,  q9 // PREP_BIAS
        vsub.i16        q3,  q3,  q9 // PREP_BIAS
.endif
        subs            \h,  \h,  #2
        vst1.16         {q2}, [\dst, :128], \d_strd
        vst1.16         {q3}, [\ds2, :128], \d_strd
        ble             9f
        vmov            q8,  q10
        vmov            q9,  q11
        vmov            q10, q12
        b               8b
9:
        subs            \w,  \w,  #8
        ble             0f
        asr             \s_strd,  \s_strd,  #1
        asr             \d_strd,  \d_strd,  #1
        mls             \src,  \s_strd,  \my,  \src
        mls             \dst,  \d_strd,  \my,  \dst
        sub             \src,  \src,  \s_strd,  lsl #2
        mov             \h,  \my
        add             \src,  \src,  #16
        add             \dst,  \dst,  #16
        b               164b
0:
        pop             {r4-r11,pc}

880:    // 8x8, 8x16, ..., 16x8, ..., 32x8, ... hv
640:
1280:
        vpush           {q4-q7}
        vld1.8          {d0}, [\mx, :64]
        vld1.8          {d2}, [\my, :64]
        sub             \src,  \src,  #6
        sub             \src,  \src,  \s_strd
        sub             \src,  \src,  \s_strd, lsl #1
        vmovl.s8        q0,  d0
        vmovl.s8        q1,  d2
        mov             \my, \h

168:
        add             \ds2, \dst, \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd, \d_strd, #1
        lsl             \s_strd, \s_strd, #1

        vld1.16         {q11, q12}, [\src], \s_strd
        vmull.s16       q2,  d22, d0[0]
        vmull.s16       q3,  d23, d0[0]
        vdup.32         q14, r12      // -(6-intermediate_bits)
.irpc i, 1234567
        vext.8          q10, q11, q12, #(2*\i)
.if \i < 4
        vmlal.s16       q2,  d20, d0[\i]
        vmlal.s16       q3,  d21, d0[\i]
.else
        vmlal.s16       q2,  d20, d1[\i - 4]
        vmlal.s16       q3,  d21, d1[\i - 4]
.endif
.endr
        vrshl.s32       q2,  q2,  q14 // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q14 // -(6-intermediate_bits)
        vmovn.i32       d8,  q2
        vmovn.i32       d9,  q3

        bl              L(\type\()_8tap_filter_8)
        vmov            q5,  q11
        vmov            q6,  q12
        bl              L(\type\()_8tap_filter_8)
        vmov            q7,  q11
        vmov            q8,  q12
        bl              L(\type\()_8tap_filter_8)
        vmov            q9,  q11
        vmov            q10, q12

88:
        bl              L(\type\()_8tap_filter_8)
        vmull.s16       q2,  d8,  d2[0]
        vmull.s16       q3,  d9,  d2[0]
        vmull.s16       q13, d10, d2[0]
        vmull.s16       q14, d11, d2[0]
.ifc \type, put
        vdup.32         q4,  r8      // -(6+intermediate_bits)
.endif
        vmlal.s16       q2,  d10, d2[1]
        vmlal.s16       q3,  d11, d2[1]
        vmlal.s16       q13, d12, d2[1]
        vmlal.s16       q14, d13, d2[1]
        vmlal.s16       q2,  d12, d2[2]
        vmlal.s16       q3,  d13, d2[2]
        vmlal.s16       q13, d14, d2[2]
        vmlal.s16       q14, d15, d2[2]
        vmlal.s16       q2,  d14, d2[3]
        vmlal.s16       q3,  d15, d2[3]
        vmlal.s16       q13, d16, d2[3]
        vmlal.s16       q14, d17, d2[3]
        vmlal.s16       q2,  d16, d3[0]
        vmlal.s16       q3,  d17, d3[0]
        vmlal.s16       q13, d18, d3[0]
        vmlal.s16       q14, d19, d3[0]
        vmlal.s16       q2,  d18, d3[1]
        vmlal.s16       q3,  d19, d3[1]
        vmlal.s16       q13, d20, d3[1]
        vmlal.s16       q14, d21, d3[1]
        vmlal.s16       q2,  d20, d3[2]
        vmlal.s16       q3,  d21, d3[2]
        vmlal.s16       q13, d22, d3[2]
        vmlal.s16       q14, d23, d3[2]
        vmlal.s16       q2,  d22, d3[3]
        vmlal.s16       q3,  d23, d3[3]
        vmlal.s16       q13, d24, d3[3]
        vmlal.s16       q14, d25, d3[3]
.ifc \type, put
        vrshl.s32       q2,  q2,  q4 // -(6+intermediate_bits)
        vrshl.s32       q3,  q3,  q4 // -(6+intermediate_bits)
        vrshl.s32       q13, q13, q4 // -(6+intermediate_bits)
        vrshl.s32       q14, q14, q4 // -(6+intermediate_bits)
        vqmovun.s32     d4,  q2
        vqmovun.s32     d5,  q3
        vqmovun.s32     d6,  q13
        vqmovun.s32     d7,  q14
        vmin.u16        q2,  q2,  q15
        vmin.u16        q3,  q3,  q15
.else
        vmov.i16        q5,  #PREP_BIAS
        vrshrn.i32      d4,  q2,  #6
        vrshrn.i32      d5,  q3,  #6
        vrshrn.i32      d6,  q13, #6
        vrshrn.i32      d7,  q14, #6
        vsub.i16        q2,  q2,  q5 // PREP_BIAS
        vsub.i16        q3,  q3,  q5 // PREP_BIAS
.endif
        subs            \h,  \h,  #2
        vst1.16         {q2}, [\dst, :128], \d_strd
        vst1.16         {q3}, [\ds2, :128], \d_strd
        ble             9f
        vmov            q4,  q6
        vmov            q5,  q7
        vmov            q6,  q8
        vmov            q7,  q9
        vmov            q8,  q10
        vmov            q9,  q11
        vmov            q10, q12
        b               88b
9:
        subs            \w,  \w,  #8
        ble             0f
        asr             \s_strd,  \s_strd,  #1
        asr             \d_strd,  \d_strd,  #1
        mls             \src,  \s_strd,  \my,  \src
        mls             \dst,  \d_strd,  \my,  \dst
        sub             \src,  \src,  \s_strd,  lsl #3
        mov             \h,  \my
        add             \src,  \src,  #16
        add             \dst,  \dst,  #16
        b               168b
0:
        vpop            {q4-q7}
        pop             {r4-r11,pc}

L(\type\()_8tap_filter_8):
        vld1.16         {q13, q14}, [\sr2], \s_strd
        vmull.s16       q2,  d26, d0[0]
        vmull.s16       q3,  d27, d0[0]
.irpc i, 1234567
        vext.8          q12, q13, q14, #(2*\i)
.if \i < 4
        vmlal.s16       q2,  d24, d0[\i]
        vmlal.s16       q3,  d25, d0[\i]
.else
        vmlal.s16       q2,  d24, d1[\i - 4]
        vmlal.s16       q3,  d25, d1[\i - 4]
.endif
.endr
        vdup.32         q12, r12      // -(6-intermediate_bits)
        vld1.16         {q13, q14}, [\src], \s_strd
        vrshl.s32       q2,  q2,  q12 // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q12 // -(6-intermediate_bits)
        vmovn.i32       d4,  q2
        vmovn.i32       d5,  q3

        vmull.s16       q3,  d26, d0[0]
        vmull.s16       q11, d27, d0[0]
.irpc i, 1234567
        vext.8          q12, q13, q14, #(2*\i)
.if \i < 4
        vmlal.s16       q3,  d24, d0[\i]
        vmlal.s16       q11, d25, d0[\i]
.else
        vmlal.s16       q3,  d24, d1[\i - 4]
        vmlal.s16       q11, d25, d1[\i - 4]
.endif
.endr
        vdup.32         q13, r12      // -(6-intermediate_bits)
        vrshl.s32       q3,  q3,  q13 // -(6-intermediate_bits)
        vrshl.s32       q11, q11, q13 // -(6-intermediate_bits)

        vmovn.i32       d24, q3
        vmovn.i32       d25, q11
        vmov            q11, q2
        bx              lr
endfunc

function \type\()_bilin_16bpc_neon, export=1
        push            {r4-r11,lr}
        ldrd            r4,  r5,  [sp, #36]
        ldrd            r6,  r7,  [sp, #44]
.ifc \bdmax, r8
        ldr             r8,  [sp, #52]
.endif
        vdup.16         q1,  \mx
        vdup.16         q3,  \my
        rsb             r9,  \mx, #16
        rsb             r10, \my, #16
        vdup.16         q0,  r9
        vdup.16         q2,  r10
.ifc \type, prep
        lsl             \d_strd, \w, #1
.endif
        clz             \bdmax,   \bdmax       // bitdepth_max
        clz             r9,  \w
        sub             \bdmax,   \bdmax,  #18 // intermediate_bits = clz(bitdepth_max) - 18
        cmp             \mx, #0
        sub             r9,  r9,  #24
        rsb             r11, \bdmax, #4        // 4 - intermediate_bits
        add             r12, \bdmax, #4        // 4 + intermediate_bits
        bne             L(\type\()_bilin_h)
        cmp             \my, #0
        bne             L(\type\()_bilin_v)
        b               \type\()_neon

L(\type\()_bilin_h):
        cmp             \my, #0
        bne             L(\type\()_bilin_hv)

        adr             r10, L(\type\()_bilin_h_tbl)
        vdup.16         q15, r11               // 4 - intermediate_bits
        ldr             r9,  [r10, r9, lsl #2]
        vneg.s16        q15, q15               // -(4-intermediate_bits)
.ifc \type, put
        vdup.16         q14, \bdmax            // intermediate_bits
.else
        vmov.i16        q14, #PREP_BIAS
.endif
        add             r10, r10, r9
.ifc \type, put
        vneg.s16        q14, q14               // -intermediate_bits
.endif
        bx              r10

        .align 2
L(\type\()_bilin_h_tbl):
        .word 1280f - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_bilin_h_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_bilin_h_tbl) + CONFIG_THUMB

20:     // 2xN h
.ifc \type, put
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
2:
        vld1.16         {d16}, [\src], \s_strd
        vld1.16         {d18}, [\sr2], \s_strd
        vext.8          d17, d16, d16, #2
        vext.8          d19, d18, d18, #2
        vtrn.32         d16, d18
        vtrn.32         d17, d19
        subs            \h,  \h,  #2
        vmul.i16        d16, d16, d0
        vmla.i16        d16, d17, d2
        vrshl.u16       d16, d16, d30
        vrshl.u16       d16, d16, d28
        vst1.32         {d16[0]}, [\dst, :32], \d_strd
        vst1.32         {d16[1]}, [\ds2, :32], \d_strd
        bgt             2b
        pop             {r4-r11,pc}
.endif

40:     // 4xN h
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
4:
        vld1.16         {q8},  [\src], \s_strd
        vld1.16         {q10}, [\sr2], \s_strd
        vext.8          q9,  q8,  q8,  #2
        vext.8          q11, q10, q10, #2
        vmov            d17, d20
        vmov            d19, d22
        subs            \h,  \h,  #2
        vmul.i16        q8,  q8,  q0
        vmla.i16        q8,  q9,  q1
        vrshl.u16       q8,  q8,  q15
.ifc \type, put
        vrshl.u16       q8,  q8,  q14
.else
        vsub.i16        q8,  q8,  q14
.endif
        vst1.16         {d16}, [\dst, :64], \d_strd
        vst1.16         {d17}, [\ds2, :64], \d_strd
        bgt             4b
        pop             {r4-r11,pc}

80:     // 8xN h
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \d_strd,  \d_strd,  #1
        lsl             \s_strd,  \s_strd,  #1
8:
        vld1.16         {d16, d17, d18}, [\src], \s_strd
        vld1.16         {d20, d21, d22}, [\sr2], \s_strd
        vext.8          q9,  q8,  q9,  #2
        vext.8          q11, q10, q11, #2
        subs            \h,  \h,  #2
        vmul.i16        q8,  q8,  q0
        vmla.i16        q8,  q9,  q1
        vmul.i16        q10, q10, q0
        vmla.i16        q10, q11, q1
        vrshl.u16       q8,  q8,  q15
        vrshl.u16       q10, q10, q15
.ifc \type, put
        vrshl.u16       q8,  q8,  q14
        vrshl.u16       q10, q10, q14
.else
        vsub.i16        q8,  q8,  q14
        vsub.i16        q10, q10, q14
.endif
        vst1.16         {q8},  [\dst, :128], \d_strd
        vst1.16         {q10}, [\ds2, :128], \d_strd
        bgt             8b
        pop             {r4-r11,pc}
160:
320:
640:
1280:   // 16xN, 32xN, ... h
        vpush           {q4-q7}
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1

        sub             \s_strd,  \s_strd,  \w, lsl #1
        sub             \s_strd,  \s_strd,  #16
.ifc \type, put
        lsl             \d_strd,  \d_strd,  #1
        sub             \d_strd,  \d_strd,  \w, lsl #1
.endif
161:
        vld1.16         {q4}, [\src]!
        vld1.16         {q9}, [\sr2]!
        mov             \mx, \w

16:
        vld1.16         {q5,  q6},  [\src]!
        vld1.16         {q10, q11}, [\sr2]!
        vext.8          q7,  q4,  q5,  #2
        vext.8          q8,  q5,  q6,  #2
        vext.8          q12, q9,  q10, #2
        vext.8          q13, q10, q11, #2
        vmul.i16        q4,  q4,  q0
        vmla.i16        q4,  q7,  q1
        vmul.i16        q5,  q5,  q0
        vmla.i16        q5,  q8,  q1
        vmul.i16        q9,  q9,  q0
        vmla.i16        q9,  q12, q1
        vmul.i16        q10, q10, q0
        vmla.i16        q10, q13, q1
        vrshl.u16       q4,  q4,  q15
        vrshl.u16       q5,  q5,  q15
        vrshl.u16       q9,  q9,  q15
        vrshl.u16       q10, q10, q15
        subs            \mx, \mx, #16
.ifc \type, put
        vrshl.u16       q4,  q4,  q14
        vrshl.u16       q5,  q5,  q14
        vrshl.u16       q9,  q9,  q14
        vrshl.u16       q10, q10, q14
.else
        vsub.i16        q4,  q4,  q14
        vsub.i16        q5,  q5,  q14
        vsub.i16        q9,  q9,  q14
        vsub.i16        q10, q10, q14
.endif
        vst1.16         {q4, q5},  [\dst, :128]!
        vst1.16         {q9, q10}, [\ds2, :128]!
        ble             9f

        vmov            q4,  q6
        vmov            q9,  q11
        b               16b

9:
        add             \dst,  \dst,  \d_strd
        add             \ds2,  \ds2,  \d_strd
        add             \src,  \src,  \s_strd
        add             \sr2,  \sr2,  \s_strd

        subs            \h,  \h,  #2
        bgt             161b
        vpop            {q4-q7}
        pop             {r4-r11,pc}


L(\type\()_bilin_v):
        cmp             \h,  #4
        adr             r10, L(\type\()_bilin_v_tbl)
.ifc \type, prep
        vdup.16         q15, r11      // 4 - intermediate_bits
.endif
        ldr             r9,  [r10, r9, lsl #2]
.ifc \type, prep
        vmov.i16        q14, #PREP_BIAS
        vneg.s16        q15, q15      // -(4-intermediate_bits)
.endif
        add             r10, r10, r9
        bx              r10

        .align 2
L(\type\()_bilin_v_tbl):
        .word 1280f - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_bilin_v_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_bilin_v_tbl) + CONFIG_THUMB

20:     // 2xN v
.ifc \type, put
        cmp             \h,  #2
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1
        lsl             \d_strd,  \d_strd,  #1

        // 2x2 v
        vld1.32         {d16[]}, [\src], \s_strd
        bgt             24f
        vld1.32         {d17[]}, [\sr2], \s_strd
        vld1.32         {d18[]}, [\src], \s_strd
        vext.8          d16, d16, d17, #4
        vext.8          d17, d17, d18, #4
        vmul.i16        d16, d16, d4
        vmla.i16        d16, d17, d6
        vrshr.u16       d16, d16, #4
        vst1.32         {d16[0]}, [\dst, :32]
        vst1.32         {d16[1]}, [\ds2, :32]
        pop             {r4-r11,pc}
24:     // 2x4, 2x8, ... v
        vld1.32         {d17[]}, [\sr2], \s_strd
        vld1.32         {d18[]}, [\src], \s_strd
        vld1.32         {d19[]}, [\sr2], \s_strd
        vld1.32         {d20[]}, [\src], \s_strd
        vext.8          d16, d16, d17, #4
        vext.8          d17, d17, d18, #4
        vext.8          d18, d18, d19, #4
        vext.8          d19, d19, d20, #4
        vswp            d17, d18
        vmul.i16        q8,  q8,  q2
        vmla.i16        q8,  q9,  q3
        subs            \h,  \h,  #4
        vrshr.u16       q8,  q8,  #4
        vst1.32         {d16[0]}, [\dst, :32], \d_strd
        vst1.32         {d16[1]}, [\ds2, :32], \d_strd
        vst1.32         {d17[0]}, [\dst, :32], \d_strd
        vst1.32         {d17[1]}, [\ds2, :32], \d_strd
        ble             0f
        vmov            d16, d20
        b               24b
0:
        pop             {r4-r11,pc}
.endif

40:     // 4xN v
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1
        lsl             \d_strd,  \d_strd,  #1
        vld1.16         {d16}, [\src], \s_strd
4:
        vld1.16         {d17}, [\sr2], \s_strd
        vld1.16         {d19}, [\src], \s_strd
        vmov            d18, d17
        vmul.i16        q8,  q8,  q2
        vmla.i16        q8,  q9,  q3
        subs            \h,  \h,  #2
.ifc \type, put
        vrshr.u16       q8,  q8,  #4
.else
        vrshl.u16       q8,  q8,  q15
        vsub.i16        q8,  q8,  q14
.endif
        vst1.16         {d16}, [\dst, :64], \d_strd
        vst1.16         {d17}, [\ds2, :64], \d_strd
        ble             0f
        vmov            d16, d19
        b               4b
0:
        pop             {r4-r11,pc}

80:     // 8xN v
        add             \ds2,  \dst,  \d_strd
        add             \sr2,  \src,  \s_strd
        lsl             \s_strd,  \s_strd,  #1
        lsl             \d_strd,  \d_strd,  #1
        vld1.16         {q8},  [\src], \s_strd
8:
        vld1.16         {q9},  [\sr2], \s_strd
        vld1.16         {q10}, [\src], \s_strd
        vmul.i16        q8,  q8,  q2
        vmla.i16        q8,  q9,  q3
        vmul.i16        q9,  q9,  q2
        vmla.i16        q9,  q10, q3
        subs            \h,  \h,  #2
.ifc \type, put
        vrshr.u16       q8,  q8,  #4
        vrshr.u16       q9,  q9,  #4
.else
        vrshl.u16       q8,  q8,  q15
        vrshl.u16       q9,  q9,  q15
        vsub.i16        q8,  q8,  q14
        vsub.i16        q9,  q9,  q14
.endif
        vst1.16         {q8}, [\dst, :128], \d_strd
        vst1.16         {q9}, [\ds2, :128], \d_strd
        ble             0f
        vmov            q8,  q10
        b               8b
0:
        pop             {r4-r11,pc}

160:    // 16xN, 32xN, ...
320:
640:
1280:
        mov             \my, \h
1:
        add             \ds2, \dst, \d_strd
        add             \sr2, \src, \s_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1

        vld1.16         {q8,  q9},  [\src], \s_strd
2:
        vld1.16         {q10, q11}, [\sr2], \s_strd
        vld1.16         {q12, q13}, [\src], \s_strd
        vmul.i16        q8,  q8,  q2
        vmla.i16        q8,  q10, q3
        vmul.i16        q9,  q9,  q2
        vmla.i16        q9,  q11, q3
        vmul.i16        q10, q10, q2
        vmla.i16        q10, q12, q3
        vmul.i16        q11, q11, q2
        vmla.i16        q11, q13, q3
        subs            \h,  \h,  #2
.ifc \type, put
        vrshr.u16       q8,  q8,  #4
        vrshr.u16       q9,  q9,  #4
        vrshr.u16       q10, q10, #4
        vrshr.u16       q11, q11, #4
.else
        vrshl.u16       q8,  q8,  q15
        vrshl.u16       q9,  q9,  q15
        vrshl.u16       q10, q10, q15
        vrshl.u16       q11, q11, q15
        vsub.i16        q8,  q8,  q14
        vsub.i16        q9,  q9,  q14
        vsub.i16        q10, q10, q14
        vsub.i16        q11, q11, q14
.endif
        vst1.16         {q8,  q9},  [\dst, :128], \d_strd
        vst1.16         {q10, q11}, [\ds2, :128], \d_strd
        ble             9f
        vmov            q8,  q12
        vmov            q9,  q13
        b               2b
9:
        subs            \w,  \w,  #16
        ble             0f
        asr             \s_strd, \s_strd, #1
        asr             \d_strd, \d_strd, #1
        mls             \src, \s_strd, \my, \src
        mls             \dst, \d_strd, \my, \dst
        sub             \src, \src, \s_strd, lsl #1
        mov             \h,  \my
        add             \src, \src, #32
        add             \dst, \dst, #32
        b               1b
0:
        pop             {r4-r11,pc}

L(\type\()_bilin_hv):
        adr             r10, L(\type\()_bilin_hv_tbl)
        vdup.16         q15, r11          // 4 - intermediate_bits
        ldr             r9,  [r10, r9, lsl #2]
        vneg.s16        q15, q15          // -(4-intermediate_bits)
.ifc \type, put
        vdup.32         q14, r12          // 4 + intermediate_bits
.else
        vmov.i16        q14, #PREP_BIAS
.endif
        add             r10, r10, r9
.ifc \type, put
        vneg.s32        q14, q14          // -(4+intermediate_bits)
.endif
        bx              r10

        .align 2
L(\type\()_bilin_hv_tbl):
        .word 1280f - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 640f  - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 320f  - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 160f  - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 80f   - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 40f   - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB
        .word 20f   - L(\type\()_bilin_hv_tbl) + CONFIG_THUMB

20:     // 2xN hv
.ifc \type, put
        add             \sr2, \src, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1

        vld1.16         {d20}, [\src], \s_strd
        vext.8          d21, d20, d20, #2
        vmul.i16        d16, d20, d0
        vmla.i16        d16, d21, d2
        vrshl.u16       d16, d16, d30
        vext.8          d16, d16, d16, #4

2:
        vld1.16         {d20}, [\sr2], \s_strd
        vld1.16         {d22}, [\src], \s_strd
        vext.8          d21, d20, d20, #2
        vext.8          d23, d22, d22, #2
        vtrn.32         d20, d22
        vtrn.32         d21, d23
        vmul.i16        d18, d20, d0
        vmla.i16        d18, d21, d2
        vrshl.u16       d18, d18, d30

        vext.8          d16, d16, d18, #4

        vmull.u16       q8,  d16, d4
        vmlal.u16       q8,  d18, d6
        vrshl.u32       q8,  q8,  q14
        vmovn.i32       d16, q8
        subs            \h,  \h,  #2
        vst1.32         {d16[0]}, [\dst, :32], \d_strd
        vst1.32         {d16[1]}, [\ds2, :32], \d_strd
        ble             0f
        vmov            d16, d18
        b               2b
0:
        pop             {r4-r11,pc}
.endif

40:     // 4xN hv
        add             \sr2, \src, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1

        vld1.16         {q10}, [\src], \s_strd
        vext.8          d21, d20, d21, #2
        vmul.i16        d16, d20, d0
        vmla.i16        d16, d21, d2
        vrshl.u16       d16, d16, d30

4:
        vld1.16         {q10}, [\sr2], \s_strd
        vld1.16         {q11}, [\src], \s_strd
        vext.8          d21, d20, d21, #2
        vext.8          d23, d22, d23, #2
        vswp            d21, d22
        vmul.i16        q9,  q10, q0
        vmla.i16        q9,  q11, q1
        vrshl.u16       q9,  q9,  q15

        vmull.u16       q10, d16, d4
        vmlal.u16       q10, d18, d6
        vmull.u16       q11, d18, d4
        vmlal.u16       q11, d19, d6
.ifc \type, put
        vrshl.u32       q10, q10, q14
        vrshl.u32       q11, q11, q14
        vmovn.i32       d20, q10
        vmovn.i32       d21, q11
.else
        vrshrn.i32      d20, q10, #4
        vrshrn.i32      d21, q11, #4
        vsub.i16        q10, q10, q14
.endif
        subs            \h,  \h,  #2
        vst1.16         {d20}, [\dst, :64], \d_strd
        vst1.16         {d21}, [\ds2, :64], \d_strd
        ble             0f
        vmov            d16, d19
        b               4b
0:
        pop             {r4-r11,pc}

80:     // 8xN, 16xN, ... hv
160:
320:
640:
1280:
        mov             \my, \h

1:
        add             \sr2, \src, \s_strd
        add             \ds2, \dst, \d_strd
        lsl             \s_strd, \s_strd, #1
        lsl             \d_strd, \d_strd, #1

        vld1.16         {d20, d21, d22}, [\src], \s_strd
        vext.8          q11, q10, q11, #2
        vmul.i16        q8,  q10, q0
        vmla.i16        q8,  q11, q1
        vrshl.u16       q8,  q8,  q15

2:
        vld1.16         {d20, d21, d22}, [\sr2], \s_strd
        vld1.16         {d24, d25, d26}, [\src], \s_strd
        vext.8          q11, q10, q11, #2
        vext.8          q13, q12, q13, #2
        vmul.i16        q9,  q10, q0
        vmla.i16        q9,  q11, q1
        vmul.i16        q10, q12, q0
        vmla.i16        q10, q13, q1
        vrshl.u16       q9,  q9,  q15
        vrshl.u16       q10, q10, q15

        vmull.u16       q11, d16, d4
        vmlal.u16       q11, d18, d6
        vmull.u16       q12, d17, d4
        vmlal.u16       q12, d19, d6
        vmull.u16       q8,  d18, d4
        vmlal.u16       q8,  d20, d6
        vmull.u16       q9,  d19, d4
        vmlal.u16       q9,  d21, d6
.ifc \type, put
        vrshl.u32       q11, q11, q14
        vrshl.u32       q12, q12, q14
        vrshl.u32       q8,  q8,  q14
        vrshl.u32       q9,  q9,  q14
        vmovn.i32       d22, q11
        vmovn.i32       d23, q12
        vmovn.i32       d16, q8
        vmovn.i32       d17, q9
.else
        vrshrn.i32      d22, q11, #4
        vrshrn.i32      d23, q12, #4
        vrshrn.i32      d16, q8,  #4
        vrshrn.i32      d17, q9,  #4
        vsub.i16        q11, q11, q14
        vsub.i16        q8,  q8,  q14
.endif
        subs            \h,  \h,  #2
        vst1.16         {q11}, [\dst, :128], \d_strd
        vst1.16         {q8},  [\ds2, :128], \d_strd
        ble             9f
        vmov            q8,  q10
        b               2b
9:
        subs            \w,  \w,  #8
        ble             0f
        asr             \s_strd,  \s_strd,  #1
        asr             \d_strd,  \d_strd,  #1
        mls             \src,  \s_strd,  \my,  \src
        mls             \dst,  \d_strd,  \my,  \dst
        sub             \src,  \src,  \s_strd,  lsl #1
        mov             \h,  \my
        add             \src,  \src,  #16
        add             \dst,  \dst,  #16
        b               1b
0:
        pop             {r4-r11,pc}
endfunc
.endm

filter_fn put,  r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10
filter_fn prep, r0, r8, r1, r2, r3, r4, r5, r6, r7, r9, r10

.macro load_filter_ptr src
        asr             r12, \src, #10
        add             r12, r11, r12, lsl #3
.endm

.macro load_filter_coef dst, src, inc
        add             \src, \src, \inc
        vld1.8          {\dst}, [r12, :64]
.endm

.macro load_filter_row dst, src, inc
        load_filter_ptr \src
        load_filter_coef \dst, \src, \inc
.endm

function warp_filter_horz_neon
        load_filter_ptr r5                  // filter 0
        vld1.16         {q6,q7}, [r2], r3

        load_filter_coef d0, r5,  r7        // filter 0
        load_filter_row d2,  r5,  r7        // filter 1
        vmovl.s8        q0,  d0             // filter 0
        vext.8          q3,  q6,  q7,  #2*1 // filter 1 pixels
        vmovl.s8        q1,  d2             // filter 1

        vmull.s16       q4,  d12, d0        // filter 0 output (0-3)
        vmull.s16       q5,  d13, d1        // filter 0 output (4-7)

        load_filter_ptr r5                  // filter 2

        vmull.s16       q2,  d6,  d2        // filter 1 output (0-3)
        vmull.s16       q3,  d7,  d3        // filter 1 output (4-7)

        load_filter_coef d0, r5,  r7        // filter 2

        vpadd.i32       d8,  d8,  d9        // half pixel 0 (2x32)
        vpadd.i32       d9,  d10, d11       // half pixel 0 (2x32)

        load_filter_ptr r5                  // filter 3

        vpadd.i32       d4,  d4,  d5        // half pixel 1 (2x32)
        vpadd.i32       d5,  d6,  d7        // half pixel 1 (2x32)

        vmovl.s8        q0,  d0             // filter 2
        vext.8          q3,  q6,  q7,  #2*2 // filter 2 pixels

        vpadd.i32       d8,  d8,  d9        // pixel 0 (2x32)
        vpadd.i32       d9,  d4,  d5        // pixel 1 (2x32)

        load_filter_coef d2, r5,  r7        // filter 3

        vmull.s16       q2,  d6,  d0        // filter 2 output (0-3)
        vmull.s16       q3,  d7,  d1        // filter 2 output (4-7)

        load_filter_ptr r5                  // filter 4

        vpadd.i32       d8,  d8,  d9        // pixel 0,1

        vpadd.i32       d9,  d4,  d5        // half pixel 2 (2x32)
        vpadd.i32       d10, d6,  d7        // half pixel 2 (2x32)

        vmovl.s8        q1,  d2             // filter 3
        vext.8          q3,  q6,  q7,  #2*3 // filter 3 pixels

        load_filter_coef d0, r5,  r7        // filter 4

        vpadd.i32       d9,  d9,  d10       // pixel 2 (2x32)

        vmull.s16       q2,  d6,  d2        // filter 3 output (0-3)
        vmull.s16       q3,  d7,  d3        // filter 3 output (4-7)

        vmovl.s8        q0,  d0             // filter 4
        load_filter_ptr r5                  // filter 5

        vpadd.i32       d10, d4,  d5        // half pixel 3 (2x32)
        vpadd.i32       d11, d6,  d7        // half pixel 3 (2x32)

        vext.8          q3,  q6,  q7,  #2*4 // filter 4 pixels
        load_filter_coef d2, r5,  r7        // filter 5

        vpadd.i32       d10, d10, d11       // pixel 3 (2x32)

        vpadd.i32       d9,  d9,  d10       // pixel 2,3

        vmull.s16       q2,  d6,  d0        // filter 4 output (0-3)
        vmull.s16       q3,  d7,  d1        // filter 4 output (4-7)

        vmovl.s8        q1,  d2             // filter 5
        load_filter_ptr r5                  // filter 6

        vpadd.i32       d10, d4,  d5        // half pixel 4 (2x32)
        vpadd.i32       d11, d6,  d7        // half pixel 4 (2x32)

        vext.8          q3,  q6,  q7,  #2*5 // filter 5 pixels
        load_filter_coef d0, r5,  r7        // filter 6

        vpadd.i32       d10, d10, d11       // pixel 4 (2x32)

        vmull.s16       q2,  d6,  d2        // filter 5 output (0-3)
        vmull.s16       q3,  d7,  d3        // filter 5 output (4-7)

        vmovl.s8        q0,  d0             // filter 6
        load_filter_ptr r5                  // filter 7

        vpadd.i32       d4,  d4,  d5        // half pixel 5 (2x32)
        vpadd.i32       d5,  d6,  d7        // half pixel 5 (2x32)

        vext.8          q3,  q6,  q7,  #2*6 // filter 6 pixels
        load_filter_coef d2, r5,  r7        // filter 7

        vpadd.i32       d11, d4,  d5        // pixel 5 (2x32)

        vmull.s16       q2,  d6,  d0        // filter 6 output (0-3)
        vmull.s16       q3,  d7,  d1        // filter 6 output (4-7)

        vmovl.s8        q1,  d2             // filter 7

        vpadd.i32       d10, d10, d11       // pixel 4,5

        vpadd.i32       d4,  d4,  d5        // half pixel 6 (2x32)
        vpadd.i32       d5,  d6,  d7        // half pixel 6 (2x32)

        vext.8          q3,  q6,  q7,  #2*7 // filter 7 pixels

        vpadd.i32       d11, d4,  d5        // pixel 6 (2x32)

        vmull.s16       q2,  d6,  d2        // filter 7 output (0-3)
        vmull.s16       q3,  d7,  d3        // filter 7 output (4-7)

        vld1.32         {d14[],d15[]}, [sp] // -(7 - intermediate_bits)

        vpadd.i32       d4,  d4,  d5        // half pixel 7 (2x32)
        vpadd.i32       d5,  d6,  d7        // half pixel 7 (2x32)

        sub             r5,  r5,  r7, lsl #3

        vpadd.i32       d4,  d4,  d5        // pixel 7 (2x32)

        add             r5,  r5,  r8

        vpadd.i32       d11, d11, d4        // pixel 6,7

        vrshl.s32       q4,  q4,  q7        // -(7 - intermediate_bits)
        vrshl.s32       q5,  q5,  q7        // -(7 - intermediate_bits)

        bx              lr
endfunc

// void dav1d_warp_affine_8x8_16bpc_neon(
//         pixel *dst, const ptrdiff_t dst_stride,
//         const pixel *src, const ptrdiff_t src_stride,
//         const int16_t *const abcd, int mx, int my,
//         const int bitdepth_max)
.macro warp t
function warp_affine_8x8\t\()_16bpc_neon, export=1
        push            {r4-r11,lr}
        vpush           {q4-q7}
        ldrd            r4,  r5,  [sp, #100]
        ldrd            r6,  r7,  [sp, #108]
        sub             sp,  sp,  #8

        clz             r7,  r7
                                      // intermediate_bits = clz(bitdepth_max) - 18
.ifb \t
        sub             r8,  r7,  #11 // 7 + intermediate_bits = clz(bitdepth_max) - 18 + 7
.endif
        sub             r7,  r7,  #25 // -(7 - intermediate_bits)
.ifb \t
        neg             r8,  r8       // -(7 + intermediate_bits)
.endif
        str             r7,  [sp]     // spill -(7 - intermediate_bits) on stack
.ifb \t
        str             r8,  [sp, #4] // spill -(7 + intermediate_bits) on stack
.endif

        ldrd            r8,  r9,  [r4]
        sxth            r7,  r8
        asr             r8,  r8,  #16
        asr             r4,  r9,  #16
        sxth            r9,  r9
        mov             r10, #8
        sub             r2,  r2,  r3, lsl #1
        sub             r2,  r2,  r3
        sub             r2,  r2,  #6
        movrel          r11, X(mc_warp_filter), 64*8
.ifnb \t
        lsl             r1,  r1,  #1
.endif
        add             r5,  r5,  #512
        add             r6,  r6,  #512

        bl              warp_filter_horz_neon
        vmovn.i32       d16, q4
        vmovn.i32       d17, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d18, q4
        vmovn.i32       d19, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d20, q4
        vmovn.i32       d21, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d22, q4
        vmovn.i32       d23, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d24, q4
        vmovn.i32       d25, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d26, q4
        vmovn.i32       d27, q5
        bl              warp_filter_horz_neon
        vmovn.i32       d28, q4
        vmovn.i32       d29, q5

1:
        bl              warp_filter_horz_neon
        vmovn.i32       d30, q4
        vmovn.i32       d31, q5

        load_filter_row d8,  r6,  r9
        load_filter_row d9,  r6,  r9
        load_filter_row d10, r6,  r9
        load_filter_row d11, r6,  r9
        load_filter_row d12, r6,  r9
        load_filter_row d13, r6,  r9
        load_filter_row d14, r6,  r9
        load_filter_row d15, r6,  r9
        transpose_8x8b  q4,  q5,  q6,  q7,  d8,  d9,  d10, d11, d12, d13, d14, d15
        vmovl.s8        q1,  d8
        vmovl.s8        q2,  d9
        vmovl.s8        q3,  d10
        vmovl.s8        q4,  d11
        vmovl.s8        q5,  d12
        vmovl.s8        q6,  d13

        sub             r6,  r6,  r9, lsl #3

        // This ordering of vmull/vmlal is highly beneficial for
        // Cortex A8/A9/A53 here, but harmful for Cortex A7.
        vmull.s16       q0,  d16, d2
        vmlal.s16       q0,  d18, d4
        vmlal.s16       q0,  d20, d6
        vmlal.s16       q0,  d22, d8
        vmlal.s16       q0,  d24, d10
        vmlal.s16       q0,  d26, d12
        vmull.s16       q1,  d17, d3
        vmlal.s16       q1,  d19, d5
        vmlal.s16       q1,  d21, d7
        vmlal.s16       q1,  d23, d9
        vmlal.s16       q1,  d25, d11
        vmlal.s16       q1,  d27, d13

        vmovl.s8        q2,  d14
        vmovl.s8        q3,  d15

        vmlal.s16       q0,  d28, d4
        vmlal.s16       q0,  d30, d6
        vmlal.s16       q1,  d29, d5
        vmlal.s16       q1,  d31, d7

.ifb \t
        ldr             lr,  [sp, #4]   // -(7 + intermediate_bits)
        ldr             r12, [sp, #120] // bitdepth_max
        vdup.32         q2,  lr         // -(7 + intermediate_bits)
        vdup.16         q3,  r12        // bitdepth_max
.endif

        vmov            q8,  q9
        vmov            q9,  q10
.ifb \t
        vrshl.s32       q0,  q0,  q2    // -(7 + intermediate_bits)
        vrshl.s32       q1,  q1,  q2    // -(7 + intermediate_bits)
.else
        vrshrn.s32      d0,  q0,  #7
        vrshrn.s32      d1,  q1,  #7
        vmov.i16        q3,  #PREP_BIAS
.endif
        vmov            q10, q11
.ifb \t
        vqmovun.s32     d0,  q0
        vqmovun.s32     d1,  q1
.else
        vsub.i16        q0,  q0,  q3    // PREP_BIAS
.endif
        vmov            q11, q12
        vmov            q12, q13
.ifb \t
        vmin.u16        q0,  q0,  q3    // bitdepth_max
.endif
        vmov            q13, q14
        vmov            q14, q15
        subs            r10, r10, #1
        vst1.16         {q0}, [r0, :128], r1

        add             r6,  r6,  r4
        bgt             1b

        add             sp,  sp,  #8
        vpop            {q4-q7}
        pop             {r4-r11,pc}
endfunc
.endm

warp
warp t

// void dav1d_emu_edge_16bpc_neon(
//         const intptr_t bw, const intptr_t bh,
//         const intptr_t iw, const intptr_t ih,
//         const intptr_t x, const intptr_t y,
//         pixel *dst, const ptrdiff_t dst_stride,
//         const pixel *ref, const ptrdiff_t ref_stride)
function emu_edge_16bpc_neon, export=1
        push            {r4-r11,lr}
        ldrd            r4,  r5,  [sp, #36]
        ldrd            r6,  r7,  [sp, #44]
        ldrd            r8,  r9,  [sp, #52]

        // ref += iclip(y, 0, ih - 1) * PXSTRIDE(ref_stride)
        // ref += iclip(x, 0, iw - 1)
        sub             r12, r3,  #1           // ih - 1
        cmp             r5,  r3
        sub             lr,  r2,  #1           // iw - 1
        it              lt
        movlt           r12, r5                // min(y, ih - 1)
        cmp             r4,  r2
        bic             r12, r12, r12, asr #31 // max(min(y, ih - 1), 0)
        it              lt
        movlt           lr,  r4                // min(x, iw - 1)
        bic             lr,  lr,  lr,  asr #31 // max(min(x, iw - 1), 0)
        mla             r8,  r12, r9,  r8      // ref += iclip() * stride
        add             r8,  r8,  lr,  lsl #1  // ref += iclip()

        // bottom_ext = iclip(y + bh - ih, 0, bh - 1)
        // top_ext = iclip(-y, 0, bh - 1)
        add             r10, r5,  r1           // y + bh
        neg             r5,  r5                // -y
        sub             r10, r10, r3           // y + bh - ih
        sub             r12, r1,  #1           // bh - 1
        cmp             r10, r1
        bic             r5,  r5,  r5,  asr #31 // max(-y, 0)
        it              ge
        movge           r10, r12               // min(y + bh - ih, bh-1)
        cmp             r5,  r1
        bic             r10, r10, r10, asr #31 // max(min(y + bh - ih, bh-1), 0)
        it              ge
        movge           r5,  r12               // min(max(-y, 0), bh-1)

        // right_ext = iclip(x + bw - iw, 0, bw - 1)
        // left_ext = iclip(-x, 0, bw - 1)
        add             r11, r4,  r0           // x + bw
        neg             r4,  r4                // -x
        sub             r11, r11, r2           // x + bw - iw
        sub             lr,  r0,  #1           // bw - 1
        cmp             r11, r0
        bic             r4,  r4,  r4,  asr #31 // max(-x, 0)
        it              ge
        movge           r11, lr                // min(x + bw - iw, bw-1)
        cmp             r4,  r0
        bic             r11, r11, r11, asr #31 // max(min(x + bw - iw, bw-1), 0)
        it              ge
        movge           r4,  lr                // min(max(-x, 0), bw - 1)

        // center_h = bh - top_ext - bottom_ext
        // dst += top_ext * PXSTRIDE(dst_stride)
        // center_w = bw - left_ext - right_ext
        sub             r1,  r1,  r5           // bh - top_ext
        mla             r6,  r5,  r7,  r6
        sub             r2,  r0,  r4           // bw - left_ext
        sub             r1,  r1,  r10          // center_h = bh - top_ext - bottom_ext
        sub             r2,  r2,  r11          // center_w = bw - left_ext - right_ext

        mov             r0,  r6                // backup of dst

.macro v_loop need_left, need_right
0:
.if \need_left
        vld1.16         {d0[], d1[]}, [r8]
        mov             r12, r6                // out = dst
        mov             r3,  r4
        vmov            q1,  q0
1:
        subs            r3,  r3,  #16
        vst1.16         {q0, q1}, [r12, :128]!
        bgt             1b
.endif
        mov             lr,  r8
        add             r12, r6,  r4,  lsl #1  // out = dst + left_ext
        mov             r3,  r2
1:
        vld1.16         {q0, q1}, [lr]!
        subs            r3,  r3,  #32
        vld1.16         {q2, q3}, [lr]!
.if \need_left
        vst1.16         {q0, q1}, [r12]!
        vst1.16         {q2, q3}, [r12]!
.else
        vst1.16         {q0, q1}, [r12, :128]!
        vst1.16         {q2, q3}, [r12, :128]!
.endif
        bgt             1b
.if \need_right
        add             r3,  r8,  r2,  lsl #1  // in + center_w
        sub             r3,  r3,  #2           // in + center_w - 1
        add             r12, r6,  r4,  lsl #1  // dst + left_ext
        vld1.16         {d0[], d1[]}, [r3]
        add             r12, r12, r2,  lsl #1  // out = dst + left_ext + center_w
        mov             r3,  r11
        vmov            q1,  q0
1:
        subs            r3,  r3,  #16
        vst1.16         {q0, q1}, [r12]!
        bgt             1b
.endif

        subs            r1,  r1,  #1           // center_h--
        add             r6,  r6,  r7
        add             r8,  r8,  r9
        bgt             0b
.endm

        cmp             r4,  #0
        beq             2f
        // need_left
        cmp             r11, #0
        beq             3f
        // need_left + need_right
        v_loop          1,   1
        b               5f

2:
        // !need_left
        cmp             r11, #0
        beq             4f
        // !need_left + need_right
        v_loop          0,   1
        b               5f

3:
        // need_left + !need_right
        v_loop          1,   0
        b               5f

4:
        // !need_left + !need_right
        v_loop          0,   0

5:
        cmp             r10, #0
        // Storing the original dst in r0 overwrote bw, recalculate it here
        add             r2,  r2,  r4           // center_w + left_ext
        add             r2,  r2,  r11          // bw = center_w + left_ext + right_ext

        beq             3f
        // need_bottom
        sub             r8,  r6,  r7           // ref = dst - stride
        mov             r4,  r2
        sub             r12, r7,  #32
1:
        vld1.16         {q0, q1}, [r8, :128]!
        mov             r3,  r10
        vld1.16         {q2, q3}, [r8, :128]!
2:
        vst1.16         {q0, q1}, [r6, :128]!
        subs            r3,  r3,  #1
        vst1.16         {q2, q3}, [r6, :128], r12
        bgt             2b
        mls             r6,  r7,  r10, r6      // dst -= bottom_ext * stride
        subs            r4,  r4,  #32          // bw -= 32
        add             r6,  r6,  #64          // dst += 32
        bgt             1b

3:
        cmp             r5,  #0
        beq             3f
        // need_top
        mls             r6,  r7,  r5,  r0      // dst = stored_dst - top_ext * stride
        sub             r12, r7,  #32
1:
        vld1.16         {q0, q1}, [r0, :128]!
        mov             r3,  r5
        vld1.16         {q2, q3}, [r0, :128]!
2:
        vst1.16         {q0, q1}, [r6, :128]!
        subs            r3,  r3,  #1
        vst1.16         {q2, q3}, [r6, :128], r12
        bgt             2b
        mls             r6,  r7,  r5,  r6      // dst -= top_ext * stride
        subs            r2,  r2,  #32          // bw -= 32
        add             r6,  r6,  #64          // dst += 32
        bgt             1b

3:
        pop             {r4-r11,pc}
endfunc
