{% comment %}
// vim: set syntax=asm :

System V ABI:
    args: rdi, rsi, rdx, rcx, r8, r9
    preserve: rbx, rsp, rbp, r12, r13, r14, r15
    scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11
    return: rax (+rdx)

Windows ABI:
    args: RCX, RDX, R8, R9
    preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15
    scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15
    return: rax (+rdx)

{% endcomment %}

{% if msvc %}

_text segment
fma_tanh_f32_{{suffix}} proc

{% else %}

.intel_syntax noprefix
.text
.p2align 5
.globl {{G}}fma_tanh_f32_{{suffix}}
{{G}}fma_tanh_f32_{{suffix}}:
.cfi_startproc
{% endif %}

    push        rbp
    mov         rbp, rsp


{% if family == "windows" %}
// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch
// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers
    and rsp,-16
    lea rsp,[rsp-160]
    vmovaps [rsp], xmm6
    vmovaps [rsp+16*1],xmm7
    vmovaps [rsp+16*2],xmm8
    vmovaps [rsp+16*3],xmm9
    vmovaps [rsp+16*4],xmm10
    vmovaps [rsp+16*5],xmm11
    vmovaps [rsp+16*6],xmm12
    vmovaps [rsp+16*7],xmm13
    vmovaps [rsp+16*8],xmm14
    vmovaps [rsp+16*9],xmm15

    // move around arguments to mimick SysV rdi,rsi passing
    push        rdi
    push        rsi
    mov         rdi, rcx
    mov         rsi, rdx

{% endif %}

    push        rbx
    push        r12
    push        r13
    push        r14
    push        r15

    sub         rsp, 8

{% if family == "unix" %}
// FIXME
// .cfi_def_cfa_offset 64 
{% endif %}

    stmxcsr     [rsp + 4]
{% if msvc %}
    mov         rax, 1FC0h
{% else %}
    mov         rax, 0x1FC0
{% endif %}
    mov         [rsp], eax
    ldmxcsr     [rsp]
// ----------------------------------------------------------------------

{%capture offset%}{% if msvc %} offset {%else%} rip + {%endif%} {%endcapture%}

    cmp     rsi, 0
    je      {{L}}done

    cmp     rsi, 32
    jl      {{L}}loop_1

{{L}}loop_4:

    vmovaps         ymm4, [rdi]
    vmovaps         ymm5, [rdi + 32]
    vmovaps         ymm6, [rdi + 64]
    vmovaps         ymm7, [rdi + 96]

    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low]
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high]
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13]
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11]

    vmaxps          ymm4, ymm4, ymm0
    vmaxps          ymm5, ymm5, ymm0
    vmaxps          ymm6, ymm6, ymm0
    vmaxps          ymm7, ymm7, ymm0
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9]

    vminps          ymm4, ymm4, ymm1
    vminps          ymm5, ymm5, ymm1
    vminps          ymm6, ymm6, ymm1
    vminps          ymm7, ymm7, ymm1        // ymm4..7 <- x
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7]

    vmulps          ymm8, ymm4, ymm4
    vmulps          ymm9, ymm5, ymm5
    vmulps          ymm10, ymm6, ymm6
    vmulps          ymm11, ymm7, ymm7        // ymm8..11 <- x^2

    vmovaps         ymm12, ymm2
    vmovaps         ymm13, ymm2
    vmovaps         ymm14, ymm2
    vmovaps         ymm15, ymm2
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5]
    vfmadd132ps     ymm12, ymm3, ymm8
    vfmadd132ps     ymm13, ymm3, ymm9
    vfmadd132ps     ymm14, ymm3, ymm10
    vfmadd132ps     ymm15, ymm3, ymm11
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3]
    vfmadd132ps     ymm12, ymm0, ymm8
    vfmadd132ps     ymm13, ymm0, ymm9
    vfmadd132ps     ymm14, ymm0, ymm10
    vfmadd132ps     ymm15, ymm0, ymm11
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1]
    vfmadd132ps     ymm12, ymm1, ymm8
    vfmadd132ps     ymm13, ymm1, ymm9
    vfmadd132ps     ymm14, ymm1, ymm10
    vfmadd132ps     ymm15, ymm1, ymm11
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6]
    vfmadd132ps     ymm12, ymm2, ymm8
    vfmadd132ps     ymm13, ymm2, ymm9
    vfmadd132ps     ymm14, ymm2, ymm10
    vfmadd132ps     ymm15, ymm2, ymm11
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4]
    vfmadd132ps     ymm12, ymm3, ymm8
    vfmadd132ps     ymm13, ymm3, ymm9
    vfmadd132ps     ymm14, ymm3, ymm10
    vfmadd132ps     ymm15, ymm3, ymm11
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2]
    vfmadd132ps     ymm12, ymm0, ymm8
    vfmadd132ps     ymm13, ymm0, ymm9
    vfmadd132ps     ymm14, ymm0, ymm10
    vfmadd132ps     ymm15, ymm0, ymm11
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0]
    vmulps          ymm4, ymm4, ymm12
    vmulps          ymm5, ymm5, ymm13
    vmulps          ymm6, ymm6, ymm14
    vmulps          ymm7, ymm7, ymm15   // ymm4..7 <- num

    vmovaps         ymm12, ymm1
    vmovaps         ymm13, ymm1
    vmovaps         ymm14, ymm1
    vmovaps         ymm15, ymm1
    vfmadd132ps     ymm12, ymm2, ymm8
    vfmadd132ps     ymm13, ymm2, ymm9
    vfmadd132ps     ymm14, ymm2, ymm10
    vfmadd132ps     ymm15, ymm2, ymm11
    vfmadd132ps     ymm12, ymm3, ymm8
    vfmadd132ps     ymm13, ymm3, ymm9
    vfmadd132ps     ymm14, ymm3, ymm10
    vfmadd132ps     ymm15, ymm3, ymm11
    vfmadd132ps     ymm12, ymm0, ymm8
    vfmadd132ps     ymm13, ymm0, ymm9
    vfmadd132ps     ymm14, ymm0, ymm10
    vfmadd132ps     ymm15, ymm0, ymm11  // ymm12..14 <- denum

    vdivps          ymm4, ymm4, ymm12
    vdivps          ymm5, ymm5, ymm13
    vdivps          ymm6, ymm6, ymm14
    vdivps          ymm7, ymm7, ymm15

    vmovaps [rdi], ymm4
    vmovaps [rdi + 32], ymm5
    vmovaps [rdi + 64], ymm6
    vmovaps [rdi + 96], ymm7

    add     rdi, 128
    sub     rsi, 32
    cmp     rsi, 32
    jg      {{L}}loop_4

    cmp     rsi, 0
    je      {{L}}done

{{L}}loop_1:
    vmovaps         ymm4, [rdi]

    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low]
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high]
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13]
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11]

    vmaxps          ymm4, ymm4, ymm0
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9]

    vminps          ymm4, ymm4, ymm1        // ymm4 <- x
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7]

    vmulps          ymm8, ymm4, ymm4        // ymm8 <- x^2

    vmovaps         ymm12, ymm2
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5]
    vfmadd132ps     ymm12, ymm3, ymm8
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3]
    vfmadd132ps     ymm12, ymm0, ymm8
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1]
    vfmadd132ps     ymm12, ymm1, ymm8
    vbroadcastss    ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6]
    vfmadd132ps     ymm12, ymm2, ymm8
    vbroadcastss    ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4]
    vfmadd132ps     ymm12, ymm3, ymm8
    vbroadcastss    ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2]
    vfmadd132ps     ymm12, ymm0, ymm8
    vbroadcastss    ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0]
    vmulps          ymm4, ymm4, ymm12

    vmovaps         ymm12, ymm1
    vfmadd132ps     ymm12, ymm2, ymm8
    vfmadd132ps     ymm12, ymm3, ymm8
    vfmadd132ps     ymm12, ymm0, ymm8

    vdivps          ymm4, ymm4, ymm12

    vmovaps [rdi], ymm4
    add     rdi, 32
    sub     rsi, 8
    jnz     {{L}}loop_1

{{L}}done:

// ----------------------------------------------------------------------

    ldmxcsr     [rsp + 4]

    add         rsp, 8

    pop r15
    pop r14
    pop r13
    pop r12
    pop rbx

{% if family == "windows" %}
    pop rsi
    pop rdi

    vmovaps xmm15, [rsp+16*9]
    vmovaps xmm14, [rsp+16*8]
    vmovaps xmm13, [rsp+16*7]
    vmovaps xmm12, [rsp+16*6]
    vmovaps xmm11, [rsp+16*5]
    vmovaps xmm10, [rsp+16*4]
    vmovaps xmm9, [rsp+16*3]
    vmovaps xmm8, [rsp+16*2]
    vmovaps xmm7, [rsp+16*1]
    vmovaps xmm6, [rsp]
{% endif %}

    mov rsp, rbp
    pop rbp
    ret

{%capture float%}{% if msvc %} real4 {%else%} .float {%endif%}{%endcapture%}

{{L}}coeffs_num_low:
    {{float}} -8.9
{{L}}coeffs_num_high:
    {{float}} 8.9

{{L}}coeffs_num_alpha_13:
    {{float}} -8.488492677e-14
{{L}}coeffs_num_alpha_11:
    {{float}} 5.277853000e-11
{{L}}coeffs_num_alpha_9:
    {{float}} -2.022500419e-8
{{L}}coeffs_num_alpha_7:
    {{float}} 0.00001115424833
{{L}}coeffs_num_alpha_5:
    {{float}} 0.003103950131
{{L}}coeffs_num_alpha_3:
    {{float}} 0.1308400453
{{L}}coeffs_num_alpha_1:
    {{float}} 0.9999999934

{{L}}coeffs_num_beta_6:
    {{float}} 0.0002546136580
{{L}}coeffs_num_beta_4:
    {{float}} 0.02449515379
{{L}}coeffs_num_beta_2:
    {{float}} 0.4641733162
{{L}}coeffs_num_beta_0:
    {{float}} 1.0



{% if msvc %}
fma_tanh_f32_{{suffix}} endp
_text ends
end
{% else %}
.cfi_endproc
{% endif %}
