/*
 * Copyright © 2021, VideoLAN and dav1d authors
 * Copyright © 2021, 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"

// void dav1d_splat_mv_neon(refmvs_block **rr, const refmvs_block *rmv,
//                          int bx4, int bw4, int bh4)

function splat_mv_neon, export=1
        push            {r4, lr}
        vld1.8          {q3},  [r1]
        ldr             r4,  [sp, #8]
        clz             r3,  r3
        adr             lr,  L(splat_tbl)
        sub             r3,  r3,  #26
        vext.8          q2,  q3,  q3,  #12
        ldr             r3,  [lr, r3, lsl #2]
        add             r2,  r2,  r2,  lsl #1
        vext.8          q0,  q2,  q3,  #4
        add             r3,  lr,  r3
        vext.8          q1,  q2,  q3,  #8
        lsl             r2,  r2,  #2
        vext.8          q2,  q2,  q3,  #12
        vmov            q3,  q0
1:
        ldr             r1,  [r0],  #4
        subs            r4,  r4,  #1
        add             r1,  r1,  r2
        bx              r3

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

10:
        vst1.8          {d0}, [r1]
        vstr            s2,  [r1, #8]
        bgt             1b
        pop             {r4, pc}
20:
        vst1.8          {q0}, [r1]
        vstr            d2,  [r1, #16]
        bgt             1b
        pop             {r4, pc}
40:
        vst1.8          {q0, q1}, [r1]!
        vst1.8          {q2},     [r1]
        bgt             1b
        pop             {r4, pc}
320:
        vst1.8          {q0, q1}, [r1]!
        vst1.8          {q2, q3}, [r1]!
        vst1.8          {q1, q2}, [r1]!
        vst1.8          {q0, q1}, [r1]!
        vst1.8          {q2, q3}, [r1]!
        vst1.8          {q1, q2}, [r1]!
160:
        vst1.8          {q0, q1}, [r1]!
        vst1.8          {q2, q3}, [r1]!
        vst1.8          {q1, q2}, [r1]!
80:
        vst1.8          {q0, q1}, [r1]!
        vst1.8          {q2, q3}, [r1]!
        vst1.8          {q1, q2}, [r1]
        bgt             1b
        pop             {r4, pc}
endfunc

const mv_tbls, align=4
        .byte           255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
        .byte           0, 1, 2, 3, 8, 0, 1, 2, 3, 8, 0, 1, 2, 3, 8, 0
        .byte           4, 5, 6, 7, 9, 4, 5, 6, 7, 9, 4, 5, 6, 7, 9, 4
        .byte           4, 5, 6, 7, 9, 4, 5, 6, 7, 9, 4, 5, 6, 7, 9, 4
endconst

const mask_mult, align=4
        .byte           1, 2, 1, 2, 0, 0, 0, 0
endconst

// void dav1d_save_tmvs_neon(refmvs_temporal_block *rp, ptrdiff_t stride,
//                           refmvs_block **rr, const uint8_t *ref_sign,
//                           int col_end8, int row_end8,
//                           int col_start8, int row_start8)
function save_tmvs_neon, export=1
        push            {r4-r11,lr}
        ldrd            r4,  r5,  [sp, #36]
        ldrd            r6,  r7,  [sp, #44]

        vmov.i8         d30, #0
        vld1.8          {d31}, [r3]
        adr             r8,  L(save_tmvs_tbl)
        movrel_local    lr,  mask_mult
        movrel_local    r12, mv_tbls
        vld1.8          {d29}, [lr]
        vext.8          d31, d30, d31, #7         // [0, ref_sign]
        mov             r3,  #5
        mul             r1,  r1,  r3              // stride *= 5
        sub             r5,  r5,  r7              // h = row_end8 - row_start8
        lsl             r7,  r7,  #1              // row_start8 <<= 1
1:
        mov             r3,  #5
        mov             r11, #12*2
        and             r9,  r7,  #30             // (y & 15) * 2
        ldr             r9,  [r2, r9, lsl #2]     // b = rr[(y & 15) * 2]
        add             r9,  r9,  #12             // &b[... + 1]
        mla             r10, r4,  r11,  r9        // end_cand_b = &b[col_end8*2 + 1]
        mla             r9,  r6,  r11,  r9        // cand_b = &b[x*2 + 1]

        mla             r3,  r6,  r3,   r0        // &rp[x]

        push            {r2,r4,r6}

2:
        ldrb            r11, [r9, #10]            // cand_b->bs
        add             lr,  r9,  #8
        vld1.8          {d0, d1}, [r9]            // cand_b->mv
        add             r11, r8,  r11, lsl #3
        vld1.16         {d2[]},  [lr]             // cand_b->ref
        ldrh            lr,  [r11]                // bw8
        mov             r2,  r8
        add             r9,  r9,  lr,  lsl #1     // cand_b += bw8*2
        cmp             r9,  r10
        vmov            d4,  d0
        bge             3f

        ldrb            r2,  [r9, #10]            // cand_b->bs
        add             lr,  r9,  #8
        vld1.8          {d6, d7}, [r9]            // cand_b->mv
        add             r2,  r8,  r2,  lsl #3
        vld1.16         {d2[1]},  [lr]            // cand_b->ref
        ldrh            lr,  [r2]                 // bw8
        add             r9,  r9,  lr,  lsl #1     // cand_b += bw8*2
        vmov            d5,  d6

3:
        vabs.s16        q2,  q2                   // abs(mv[].xy)
        vtbl.8          d2,  {d31}, d2            // ref_sign[ref]
        vshr.u16        q2,  q2,  #12             // abs(mv[].xy) >> 12
        vmull.u8        q1,  d2,  d29             // ref_sign[ref] * {1, 2}
        vceq.i32        q2,  q2,  #0              // abs(mv[].xy) <= 4096
        vmovn.i32       d4,  q2                   // abs() condition to 16 bit
        vand            d2,  d2,  d4              // h[0-3] contains conditions for mv[0-1]
        vpadd.i16       d2,  d2,  d2              // Combine condition for [1] and [0]
        vmov.u16        r4,  d2[0]                // Extract case for first block
        vmov.u16        r6,  d2[1]
        ldr             r11, [r11, #4]            // Fetch jump table entry
        ldr             r2,  [r2,  #4]
        add             r4,  r12,  r4,  lsl #4
        add             r6,  r12,  r6,  lsl #4
        vld1.8          {d2, d3}, [r4]            // Load permutation table base on case
        vld1.8          {d4, d5}, [r6]
        add             r11, r8,  r11             // Find jump table target
        add             r2,  r8,  r2
        vtbl.8          d16, {d0, d1}, d2         // Permute cand_b to output refmvs_temporal_block
        vtbl.8          d17, {d0, d1}, d3
        vtbl.8          d18, {d6, d7}, d4
        vtbl.8          d19, {d6, d7}, d5
        vmov            q0,  q8

        // q1 follows on q0 (q8), with another 3 full repetitions of the pattern.
        vext.8          q1,  q8,  q8,  #1
        vext.8          q10, q9,  q9,  #1
        // q2 ends with 3 complete repetitions of the pattern.
        vext.8          q2,  q8,  q1,  #4
        vext.8          q11, q9,  q10, #4

        blx             r11
        bge             4f  // if (cand_b >= end)
        vmov            q0,  q9
        vmov            q1,  q10
        vmov            q2,  q11
        cmp             r9,  r10
        blx             r2
        blt             2b  // if (cand_b < end)

4:
        pop             {r2,r4,r6}

        subs            r5,  r5,  #1              // h--
        add             r7,  r7,  #2              // y += 2
        add             r0,  r0,  r1              // rp += stride
        bgt             1b

        pop             {r4-r11,pc}

        .align 2
L(save_tmvs_tbl):
        .word 16 * 12
        .word 160f - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 16 * 12
        .word 160f - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 8 * 12
        .word 80f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 8 * 12
        .word 80f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 8 * 12
        .word 80f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 8 * 12
        .word 80f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 4 * 12
        .word 40f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 4 * 12
        .word 40f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 4 * 12
        .word 40f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 4 * 12
        .word 40f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 2 * 12
        .word 20f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 2 * 12
        .word 20f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 2 * 12
        .word 20f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 2 * 12
        .word 20f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 2 * 12
        .word 20f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB
        .word 1 * 12
        .word 10f  - L(save_tmvs_tbl) + CONFIG_THUMB

10:
        add             r4,  r3,  #4
        vst1.32         {d0[0]}, [r3]
        vst1.8          {d0[4]}, [r4]
        add             r3,  r3,  #5
        bx              lr
20:
        add             r4,  r3,  #8
        vst1.8          {d0}, [r3]
        vst1.16         {d1[0]}, [r4]
        add             r3,  r3,  #2*5
        bx              lr
40:
        add             r4,  r3,  #16
        vst1.8          {q0}, [r3]
        vst1.32         {d2[0]}, [r4]
        add             r3,  r3,  #4*5
        bx              lr
80:
        add             r4,  r3,  #(8*5-16)
        // This writes 6 full entries plus 2 extra bytes
        vst1.8          {q0, q1}, [r3]
        // Write the last few, overlapping with the first write.
        vst1.8          {q2}, [r4]
        add             r3,  r3,  #8*5
        bx              lr
160:
        add             r4,  r3,  #6*5
        add             r6,  r3,  #12*5
        // This writes 6 full entries plus 2 extra bytes
        vst1.8          {q0, q1}, [r3]
        // Write another 6 full entries, slightly overlapping with the first set
        vst1.8          {q0, q1}, [r4]
        add             r4,  r3,  #(16*5-16)
        // Write 8 bytes (one full entry) after the first 12
        vst1.8          {d0}, [r6]
        // Write the last 3 entries
        vst1.8          {q2}, [r4]
        add             r3,  r3,  #16*5
        bx              lr
endfunc