// vim: ft=arm

// x20..x27 are used, callee-preserved

// C tile regs: v16 to v31, (scratch)

// v8 is used, d8 (lower half) must preserved
// v0-v7 (scratch registers)
//  packed A buffering (2x8 values): alternating v0, v1 with v2, v3
//  packed B buffering (2x8 values): alternating v4, v5 with v6, v7

.text
.align 4

{% if needs_pragma == true %}
.cpu generic+fp+simd+fp16
{% endif %}
.global {{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}}
{{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}}:

    stp         x20, x21, [sp, #-16]!
    stp         x22, x23, [sp, #-16]!
    stp         x24, x25, [sp, #-16]!
    stp         x26, x27, [sp, #-16]!

    stp         d8, d9, [sp, #-16]!

{% include "dispatcher.j2" %}

.add_mat_mul:
    ldr         x2, [x0, #24]       // b
    ldp         x3, x1, [x0, #8]    // k, a

    cmp         x3, #0
    beq         .non_linear_loop

    ld1         { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64
    ldr         d4, [x2], #8

{% set packed_packed_loop1 %}
    {% include "arm64fp16_mmm_f16_32x4/loop1/naive.S.raw" %}
{% endset %}

{% set packed_packed_loop2 %}
    {% if core == "a55" %}
        {% include "arm64fp16_mmm_f16_32x4/loop2/cortex_a55.S.raw" %}
    {% else %}
        {{ packed_packed_loop1 }}
        {{ packed_packed_loop1 }}
    {% endif %}
{% endset %}

    cmp         x3, #4
    blt         .packed_packed_loop_1

.p2align 4
.packed_packed_loop_4:
    {{ packed_packed_loop2 }}
    {{ packed_packed_loop2 }}

    sub x3, x3, #4
    cmp x3, #4
    bge .packed_packed_loop_4

    cmp x3, #0
    beq .non_linear_loop

.p2align 4
.packed_packed_loop_1:
    {{ packed_packed_loop1 }}
    subs        x3, x3, #1
    bne .packed_packed_loop_1

    b   .non_linear_loop

{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %}
{% set mr = 32 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %}
{% set mr = 32 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %}
{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %}

.add_unicast:
    ldp         x5, x6, [x0, #8]
    ldp         x7, x8, [x0, #24]

    {% for col in range(0, 4) %}
        mov x4, x5
        {% for reg in range(0, 4) %}
            {% for lane in range(0, 8) %}
                ld1 {v0.h}[{{lane}}], [ x4 ], x6
            {% endfor %}
            fadd v{{ col * 4 + 16 + reg }}.8h, v{{ col * 4 + 16 + reg }}.8h, v0.8h
        {% endfor %}
        add x5, x5, x7
    {% endfor %}

    b           .non_linear_loop

.add_row_col_products:
    ldr     x2, [x0, #8]
    ldr     x3, [x0, #16]

    ld1         { v0.8h, v1.8h, v2.8h, v3.8h }, [ x2 ]
    ldr         d4, [x3]

    fmla        v16.8h, v0.8h, v4.h[0]
    fmla        v17.8h, v1.8h, v4.h[0]
    fmla        v18.8h, v2.8h, v4.h[0]
    fmla        v19.8h, v3.8h, v4.h[0]
    fmla        v20.8h, v0.8h, v4.h[1]
    fmla        v21.8h, v1.8h, v4.h[1]
    fmla        v22.8h, v2.8h, v4.h[1]
    fmla        v23.8h, v3.8h, v4.h[1]

    fmla        v24.8h, v0.8h, v4.h[2]
    fmla        v25.8h, v1.8h, v4.h[2]
    fmla        v26.8h, v2.8h, v4.h[2]
    fmla        v27.8h, v3.8h, v4.h[2]
    fmla        v28.8h, v0.8h, v4.h[3]
    fmla        v29.8h, v1.8h, v4.h[3]
    fmla        v30.8h, v2.8h, v4.h[3]
    fmla        v31.8h, v3.8h, v4.h[3]

    b           .non_linear_loop

.store:
    ldp         x5, x6, [x0, #8]            // c base ptr, rsc
    ldp         x7, x8, [x0, #24]           // csc, item_size

    cmp         x6, #2
    bne           .store_strides_generic

    {% for col in range(0, 4) %}
        str q{{ col * 4 + 16 + 0 }}, [ x5 ]
        str q{{ col * 4 + 16 + 1 }}, [ x5, #16 ]
        str q{{ col * 4 + 16 + 2 }}, [ x5, #32 ]
        str q{{ col * 4 + 16 + 3 }}, [ x5, #48 ]
        add x5, x5, x7
    {% endfor %}

    b           .non_linear_loop

.store_strides_generic:

    {% for col in range(0, 4) %}
        mov x4, x5
        {% for reg in range(0, 4) %}
            {% for lane in range(0, 8) %}
                st1 { v{{ col * 4 + 16 + reg }}.h }[{{lane}}], [ x4 ], x6
            {% endfor %}
        {% endfor %}
        add x5, x5, x7
    {% endfor %}

    b           .non_linear_loop

.return:
    ldp         d8, d9, [sp], #16

    ldp         x26, x27, [sp], #16
    ldp         x24, x25, [sp], #16
    ldp         x22, x23, [sp], #16
    ldp         x20, x21, [sp], #16

    ret

