; Copyright © 2021, VideoLAN and dav1d authors
; Copyright © 2021, Two Orioles, LLC
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
;    list of conditions and the following disclaimer.
;
; 2. Redistributions in binary form must reproduce the above copyright notice,
;    this list of conditions and the following disclaimer in the documentation
;    and/or other materials provided with the distribution.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
; ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

%include "config.asm"
%include "ext/x86/x86inc.asm"

SECTION_RODATA

wiener_shufA:  db  2,  3,  4,  5,  4,  5,  6,  7,  6,  7,  8,  9,  8,  9, 10, 11
wiener_shufB:  db  6,  7,  4,  5,  8,  9,  6,  7, 10, 11,  8,  9, 12, 13, 10, 11
wiener_shufC:  db  6,  7,  8,  9,  8,  9, 10, 11, 10, 11, 12, 13, 12, 13, 14, 15
wiener_shufD:  db  2,  3, -1, -1,  4,  5, -1, -1,  6,  7, -1, -1,  8,  9, -1, -1
wiener_shufE:  db  0,  1,  8,  9,  2,  3, 10, 11,  4,  5, 12, 13,  6,  7, 14, 15
wiener_lshuf5: db  0,  1,  0,  1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11
wiener_lshuf7: db  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  2,  3,  4,  5,  6,  7
sgr_lshuf3:    db  0,  1,  0,  1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11
sgr_lshuf5:    db  0,  1,  0,  1,  0,  1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9
pb_0to15:      db  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15

pb_m14_m13:    times 8 db -14,-13
pb_m10_m9:     times 8 db -10, -9
pb_m6_m5:      times 8 db  -6, -5
pb_m2_m1:      times 8 db  -2, -1
pb_2_3:        times 8 db   2,  3
pb_6_7:        times 8 db   6,  7
pw_256:        times 8 dw 256
pw_1023:       times 8 dw 1023
pw_164_24:     times 4 dw 164, 24
pw_455_24:     times 4 dw 455, 24
pd_8:          times 4 dd 8
pd_4096:       times 4 dd 4096
pd_34816:      times 4 dd 34816
pd_m262128:    times 4 dd -262128
pd_0xffff:     times 4 dd 0xffff
pd_0xfffffff0: times 4 dd 0xfffffff0
pf_256:        times 4 dd 256.0

wiener_shifts: dw 4, 4, 2048, 2048, 1, 1, 8192, 8192
wiener_round:  dd 1049600, 1048832

SECTION .text

%macro movif64 2 ; dst, src
 %if ARCH_X86_64
    mov             %1, %2
 %endif
%endmacro

%macro movif32 2 ; dst, src
 %if ARCH_X86_32
    mov             %1, %2
 %endif
%endmacro

INIT_XMM ssse3
%if ARCH_X86_32
DECLARE_REG_TMP 5, 6
 %if STACK_ALIGNMENT < 16
  %assign extra_stack 13*16
 %else
  %assign extra_stack 12*16
 %endif
cglobal wiener_filter7_16bpc, 4, 7, 8, -384*12-16-extra_stack, \
                              dst, stride, left, lpf, w, flt
 %if STACK_ALIGNMENT < 16
  %define lpfm        dword [esp+calloff+16*12+ 0]
  %define wm          dword [esp+calloff+16*12+ 4]
  %define hd          dword [esp+calloff+16*12+ 8]
  %define edgeb        byte [esp+calloff+16*12+12]
  %define edged       dword [esp+calloff+16*12+12]
 %else
  %define hd dword r5m
  %define edgeb byte r7m
 %endif
 %define PICmem dword [esp+calloff+4*0]
 %define t0m    dword [esp+calloff+4*1] ; wiener ring buffer pointers
 %define t1m    dword [esp+calloff+4*2]
 %define t2m    dword [esp+calloff+4*3]
 %define t3m    dword [esp+calloff+4*4]
 %define t4m    dword [esp+calloff+4*5]
 %define t5m    dword [esp+calloff+4*6]
 %define t6m    dword [esp+calloff+4*7]
 %define t2 t2m
 %define t3 t3m
 %define t4 t4m
 %define t5 t5m
 %define t6 t6m
 %define  m8 [esp+calloff+16*2]
 %define  m9 [esp+calloff+16*3]
 %define m10 [esp+calloff+16*4]
 %define m11 [esp+calloff+16*5]
 %define m12 [esp+calloff+16*6]
 %define m13 [esp+calloff+16*7]
 %define m14 [esp+calloff+16*8]
 %define m15 [esp+calloff+16*9]
 %define r10 r4
 %define base t0-wiener_shifts
 %assign calloff 0
 %if STACK_ALIGNMENT < 16
    mov             wd, [rstk+stack_offset+20]
    mov             wm, wd
    mov             r5, [rstk+stack_offset+24]
    mov             hd, r5
    mov             r5, [rstk+stack_offset+32]
    mov          edged, r5 ; edge
 %endif
%else
DECLARE_REG_TMP 8, 7, 9, 11, 12, 13, 14 ; wiener ring buffer pointers
cglobal wiener_filter7_16bpc, 4, 15, 16, -384*12-16, dst, stride, left, lpf, \
                                                     w, h, edge, flt
 %define base
%endif
%if ARCH_X86_64 || STACK_ALIGNMENT >= 16
    movifnidn       wd, wm
%endif
%if ARCH_X86_64
    mov           fltq, r6mp
    movifnidn       hd, hm
    mov          edged, r7m
    mov            t3d, r8m ; pixel_max
    movq           m13, [fltq]
    movq           m15, [fltq+16]
%else
 %if STACK_ALIGNMENT < 16
    mov             t0, [rstk+stack_offset+28]
    mov             t1, [rstk+stack_offset+36] ; pixel_max
    movq            m1, [t0]    ; fx
    movq            m3, [t0+16] ; fy
    LEA             t0, wiener_shifts
 %else
    mov           fltq, r6m
    movq            m1, [fltq]
    movq            m3, [fltq+16]
    LEA             t0, wiener_shifts
    mov             t1, r8m ; pixel_max
 %endif
    mov         PICmem, t0
%endif
    mova            m6, [base+wiener_shufA]
    mova            m7, [base+wiener_shufB]
%if ARCH_X86_64
    lea             t4, [wiener_shifts]
    add             wd, wd
    pshufd         m12, m13, q0000 ; x0 x1
    pshufd         m13, m13, q1111 ; x2 x3
    pshufd         m14, m15, q0000 ; y0 y1
    pshufd         m15, m15, q1111 ; y2 y3
    mova            m8, [wiener_shufC]
    mova            m9, [wiener_shufD]
    add           lpfq, wq
    lea             t1, [rsp+wq+16]
    add           dstq, wq
    neg             wq
    shr            t3d, 11
 %define base t4-wiener_shifts
    movd           m10, [base+wiener_round+t3*4]
    movq           m11, [base+wiener_shifts+t3*8]
    pshufd         m10, m10, q0000
    pshufd          m0, m11, q0000
    pshufd         m11, m11, q1111
    pmullw         m12, m0 ; upshift filter coefs to make the
    pmullw         m13, m0 ; horizontal downshift constant
 DEFINE_ARGS dst, stride, left, lpf, _, h, edge, _, _, _, w
 %define lpfm [rsp]
 %define base
 %define wiener_lshuf7_mem [wiener_lshuf7]
 %define pd_m262128_mem [pd_m262128]
%else
    add             wd, wd
    mova            m4, [base+wiener_shufC]
    mova            m5, [base+wiener_shufD]
    pshufd          m0, m1, q0000
    pshufd          m1, m1, q1111
    pshufd          m2, m3, q0000
    pshufd          m3, m3, q1111
    mova            m8, m4
    mova            m9, m5
    mova           m14, m2
    mova           m15, m3
    shr             t1, 11
    add           lpfq, wq
    mova            m3, [base+pd_m262128]
    movd            m4, [base+wiener_round+t1*4]
    movq            m5, [base+wiener_shifts+t1*8]
    lea             t1, [esp+extra_stack+wq+16]
    add           dstq, wq
    neg             wq
    pshufd          m4, m4, q0000
    pshufd          m2, m5, q0000
    pshufd          m5, m5, q1111
    mov             wm, wq
    pmullw          m0, m2
    pmullw          m1, m2
    mova            m2, [base+wiener_lshuf7]
 %define pd_m262128_mem [esp+calloff+16*10]
    mova pd_m262128_mem, m3
    mova           m10, m4
    mova           m11, m5
    mova           m12, m0
    mova           m13, m1
 %define wiener_lshuf7_mem [esp+calloff+16*11]
    mova wiener_lshuf7_mem, m2
%endif
    test         edgeb, 4 ; LR_HAVE_TOP
    jz .no_top
    call .h_top
    add           lpfq, strideq
    mov             t6, t1
    mov             t5, t1
    add             t1, 384*2
    call .h_top
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    mov             t4, t1
    add             t1, 384*2
    add            r10, strideq
    mov           lpfm, r10 ; below
    call .h
    mov             t3, t1
    mov             t2, t1
    dec             hd
    jz .v1
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    mov             t2, t1
    dec             hd
    jz .v2
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    dec             hd
    jz .v3
.main:
    lea             t0, [t1+384*2]
.main_loop:
    call .hv
    dec             hd
    jnz .main_loop
    test         edgeb, 8 ; LR_HAVE_BOTTOM
    jz .v3
    mov           lpfq, lpfm
    call .hv_bottom
    add           lpfq, strideq
    call .hv_bottom
.v1:
    call .v
    RET
.no_top:
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    lea            r10, [r10+strideq*2]
    mov           lpfm, r10
    call .h
    mov             t6, t1
    mov             t5, t1
    mov             t4, t1
    mov             t3, t1
    mov             t2, t1
    dec             hd
    jz .v1
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    mov             t2, t1
    dec             hd
    jz .v2
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    dec             hd
    jz .v3
    lea             t0, [t1+384*2]
    call .hv
    dec             hd
    jz .v3
    add             t0, 384*8
    call .hv
    dec             hd
    jnz .main
.v3:
    call .v
    movif32         wq, wm
.v2:
    call .v
    movif32         wq, wm
    jmp .v1
.extend_right:
%assign stack_offset stack_offset+8
%assign calloff 8
    movif32         t0, PICmem
    pxor            m0, m0
    movd            m1, wd
    mova            m2, [base+pb_0to15]
    pshufb          m1, m0
    mova            m0, [base+pb_6_7]
    psubb           m0, m1
    pminub          m0, m2
    pshufb          m3, m0
    mova            m0, [base+pb_m2_m1]
    psubb           m0, m1
    pminub          m0, m2
    pshufb          m4, m0
    mova            m0, [base+pb_m10_m9]
    psubb           m0, m1
    pminub          m0, m2
    pshufb          m5, m0
    movif32         t0, t0m
    ret
%assign stack_offset stack_offset-4
%assign calloff 4
.h:
    movif64         wq, r4
    movif32         wq, wm
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movq            m3, [leftq]
    movhps          m3, [lpfq+wq]
    add          leftq, 8
    jmp .h_main
.h_extend_left:
    mova            m3, [lpfq+wq]         ; avoid accessing memory located
    pshufb          m3, wiener_lshuf7_mem ; before the start of the buffer
    jmp .h_main
.h_top:
    movif64         wq, r4
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
.h_loop:
    movu            m3, [lpfq+wq-8]
.h_main:
    mova            m4, [lpfq+wq+0]
    movu            m5, [lpfq+wq+8]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .h_have_right
    cmp             wd, -20
    jl .h_have_right
    call .extend_right
.h_have_right:
    pshufb          m0, m3, m6
    pshufb          m1, m4, m7
    paddw           m0, m1
    pshufb          m3, m8
    pmaddwd         m0, m12
    pshufb          m1, m4, m9
    paddw           m3, m1
    pshufb          m1, m4, m6
    pmaddwd         m3, m13
    pshufb          m2, m5, m7
    paddw           m1, m2
    mova            m2, pd_m262128_mem ; (1 << 4) - (1 << 18)
    pshufb          m4, m8
    pmaddwd         m1, m12
    pshufb          m5, m9
    paddw           m4, m5
    pmaddwd         m4, m13
    paddd           m0, m2
    paddd           m1, m2
    paddd           m0, m3
    paddd           m1, m4
    psrad           m0, 4
    psrad           m1, 4
    packssdw        m0, m1
    psraw           m0, 1
    mova       [t1+wq], m0
    add             wq, 16
    jl .h_loop
    movif32         wq, wm
    ret
ALIGN function_align
.hv:
    add           lpfq, strideq
    movif64         wq, r4
    movif32        t0m, t0
    movif32        t1m, t1
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
    movq            m3, [leftq]
    movhps          m3, [lpfq+wq]
    add          leftq, 8
    jmp .hv_main
.hv_extend_left:
    mova            m3, [lpfq+wq]
    pshufb          m3, wiener_lshuf7_mem
    jmp .hv_main
.hv_bottom:
    movif64         wq, r4
    movif32        t0m, t0
    movif32        t1m, t1
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
.hv_loop:
    movu            m3, [lpfq+wq-8]
.hv_main:
    mova            m4, [lpfq+wq+0]
    movu            m5, [lpfq+wq+8]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv_have_right
    cmp             wd, -20
    jl .hv_have_right
    call .extend_right
.hv_have_right:
    movif32         t1, t4m
    movif32         t0, t2m
    pshufb          m0, m3, m6
    pshufb          m1, m4, m7
    paddw           m0, m1
    pshufb          m3, m8
    pmaddwd         m0, m12
    pshufb          m1, m4, m9
    paddw           m3, m1
    pshufb          m1, m4, m6
    pmaddwd         m3, m13
    pshufb          m2, m5, m7
    paddw           m1, m2
    mova            m2, pd_m262128_mem
    pshufb          m4, m8
    pmaddwd         m1, m12
    pshufb          m5, m9
    paddw           m4, m5
    pmaddwd         m4, m13
    paddd           m0, m2
    paddd           m1, m2
%if ARCH_X86_64
    mova            m2, [t4+wq]
    paddw           m2, [t2+wq]
    mova            m5, [t3+wq]
%else
    mova            m2, [t1+wq]
    paddw           m2, [t0+wq]
    mov             t1, t3m
    mov             t0, t5m
    mova            m5, [t1+wq]
    mov             t1, t1m
%endif
    paddd           m0, m3
    paddd           m1, m4
    psrad           m0, 4
    psrad           m1, 4
    packssdw        m0, m1
%if ARCH_X86_64
    mova            m4, [t5+wq]
    paddw           m4, [t1+wq]
    psraw           m0, 1
    paddw           m3, m0, [t6+wq]
%else
    mova            m4, [t0+wq]
    paddw           m4, [t1+wq]
    mov             t0, t0m
    mov             t1, t6m
    psraw           m0, 1
    paddw           m3, m0, [t1+wq]
%endif
    mova       [t0+wq], m0
    punpcklwd       m0, m2, m5
    pmaddwd         m0, m15
    punpckhwd       m2, m5
    pmaddwd         m2, m15
    punpcklwd       m1, m3, m4
    pmaddwd         m1, m14
    punpckhwd       m3, m4
    pmaddwd         m3, m14
    paddd           m0, m10
    paddd           m2, m10
    paddd           m0, m1
    paddd           m2, m3
    psrad           m0, 6
    psrad           m2, 6
    packssdw        m0, m2
    pmulhw          m0, m11
    pxor            m1, m1
    pmaxsw          m0, m1
    mova     [dstq+wq], m0
    add             wq, 16
    jl .hv_loop
%if ARCH_X86_64
    mov             t6, t5
    mov             t5, t4
    mov             t4, t3
    mov             t3, t2
    mov             t2, t1
    mov             t1, t0
    mov             t0, t6
%else
    mov             r4, t5m
    mov             t1, t4m
    mov            t6m, r4
    mov            t5m, t1
    mov             r4, t3m
    mov             t1, t2m
    mov            t4m, r4
    mov            t3m, t1
    mov             r4, t1m
    mov             t1, t0
    mov            t2m, r4
    mov             t0, t6m
    mov             wq, wm
%endif
    add           dstq, strideq
    ret
.v:
    movif64         wq, r4
    movif32        t0m, t0
    movif32        t1m, t1
.v_loop:
%if ARCH_X86_64
    mova            m1, [t4+wq]
    paddw           m1, [t2+wq]
    mova            m2, [t3+wq]
    mova            m4, [t1+wq]
    paddw           m3, m4, [t6+wq]
    paddw           m4, [t5+wq]
%else
    mov             t0, t4m
    mov             t1, t2m
    mova            m1, [t0+wq]
    paddw           m1, [t1+wq]
    mov             t0, t3m
    mov             t1, t1m
    mova            m2, [t0+wq]
    mova            m4, [t1+wq]
    mov             t0, t6m
    mov             t1, t5m
    paddw           m3, m4, [t0+wq]
    paddw           m4, [t1+wq]
%endif
    punpcklwd       m0, m1, m2
    pmaddwd         m0, m15
    punpckhwd       m1, m2
    pmaddwd         m1, m15
    punpcklwd       m2, m3, m4
    pmaddwd         m2, m14
    punpckhwd       m3, m4
    pmaddwd         m3, m14
    paddd           m0, m10
    paddd           m1, m10
    paddd           m0, m2
    paddd           m1, m3
    psrad           m0, 6
    psrad           m1, 6
    packssdw        m0, m1
    pmulhw          m0, m11
    pxor            m1, m1
    pmaxsw          m0, m1
    mova     [dstq+wq], m0
    add             wq, 16
    jl .v_loop
%if ARCH_X86_64
    mov             t6, t5
    mov             t5, t4
    mov             t4, t3
    mov             t3, t2
    mov             t2, t1
%else
    mov             t0, t5m
    mov             t1, t4m
    mov             r4, t3m
    mov            t6m, t0
    mov            t5m, t1
    mov            t4m, r4
    mov             r4, t2m
    mov             t1, t1m
    mov             t0, t0m
    mov            t3m, r4
    mov            t2m, t1
%endif
    add           dstq, strideq
    ret

%if ARCH_X86_32
 %if STACK_ALIGNMENT < 16
  %assign stack_size 12*16+384*8
 %else
  %assign stack_size 11*16+384*8
 %endif
cglobal wiener_filter5_16bpc, 4, 7, 8, -stack_size, dst, stride, left, \
                                                    lpf, w, flt
 %if STACK_ALIGNMENT < 16
  %define lpfm        dword [esp+calloff+4*6]
  %define wm          dword [esp+calloff+4*7]
  %define hd          dword [esp+calloff+16*10+0]
  %define edgeb        byte [esp+calloff+16*10+4]
  %define edged       dword [esp+calloff+16*10+4]
 %else
  %define hd dword r5m
  %define edgeb byte r7m
 %endif
 %define PICmem dword [esp+calloff+4*0]
 %define t0m    dword [esp+calloff+4*1] ; wiener ring buffer pointers
 %define t1m    dword [esp+calloff+4*2]
 %define t2m    dword [esp+calloff+4*3]
 %define t3m    dword [esp+calloff+4*4]
 %define t4m    dword [esp+calloff+4*5]
 %define t2 t2m
 %define t3 t3m
 %define t4 t4m
 %define  m8 [esp+calloff+16*2]
 %define  m9 [esp+calloff+16*3]
 %define m10 [esp+calloff+16*4]
 %define m11 [esp+calloff+16*5]
 %define m12 [esp+calloff+16*6]
 %define m13 [esp+calloff+16*7]
 %define m14 [esp+calloff+16*8]
 %define m15 [esp+calloff+16*9]
 %define base t0-wiener_shifts
 %assign calloff 0
 %if STACK_ALIGNMENT < 16
    mov             wd, [rstk+stack_offset+20]
    mov             wm, wd
    mov             r5, [rstk+stack_offset+24]
    mov             hd, r5
    mov             r5, [rstk+stack_offset+32]
    mov          edged, r5 ; edge
 %endif
%else
cglobal wiener_filter5_16bpc, 4, 14, 16, 384*8+16, dst, stride, left, lpf, \
                                                   w, h, edge, flt
 %define base
%endif
%if ARCH_X86_64 || STACK_ALIGNMENT >= 16
    movifnidn       wd, wm
%endif
%if ARCH_X86_64
    mov           fltq, r6mp
    movifnidn       hd, hm
    mov          edged, r7m
    mov            t3d, r8m ; pixel_max
    movq           m12, [fltq]
    movq           m14, [fltq+16]
%else
 %if STACK_ALIGNMENT < 16
    mov             t0, [rstk+stack_offset+28]
    mov             t1, [rstk+stack_offset+36] ; pixel_max
    movq            m1, [t0]    ; fx
    movq            m3, [t0+16] ; fy
    LEA             t0, wiener_shifts
 %else
    mov           fltq, r6m
    movq            m1, [fltq]
    movq            m3, [fltq+16]
    LEA             t0, wiener_shifts
    mov             t1, r8m ; pixel_max
 %endif
    mov         PICmem, t0
%endif
    mova            m5, [base+wiener_shufE]
    mova            m6, [base+wiener_shufB]
    mova            m7, [base+wiener_shufD]
%if ARCH_X86_64
    lea             t4, [wiener_shifts]
    add             wd, wd
    punpcklwd      m11, m12, m12
    pshufd         m11, m11, q1111 ; x1
    pshufd         m12, m12, q1111 ; x2 x3
    punpcklwd      m13, m14, m14
    pshufd         m13, m13, q1111 ; y1
    pshufd         m14, m14, q1111 ; y2 y3
    shr            t3d, 11
    mova            m8, [pd_m262128] ; (1 << 4) - (1 << 18)
    add           lpfq, wq
    lea             t1, [rsp+wq+16]
    add           dstq, wq
    neg             wq
 %define base t4-wiener_shifts
    movd            m9, [base+wiener_round+t3*4]
    movq           m10, [base+wiener_shifts+t3*8]
    pshufd          m9, m9, q0000
    pshufd          m0, m10, q0000
    pshufd         m10, m10, q1111
    mova           m15, [wiener_lshuf5]
    pmullw         m11, m0
    pmullw         m12, m0
 DEFINE_ARGS dst, stride, left, lpf, _, h, edge, _, _, _, w
 %define lpfm [rsp]
 %define base
%else
    add             wd, wd
    punpcklwd       m0, m1, m1
    pshufd          m0, m0, q1111 ; x1
    pshufd          m1, m1, q1111 ; x2 x3
    punpcklwd       m2, m3, m3
    pshufd          m2, m2, q1111 ; y1
    pshufd          m3, m3, q1111 ; y2 y3
    mova            m4, [base+pd_m262128] ; (1 << 4) - (1 << 18)
    mova           m13, m2
    mova           m14, m3
    mova            m8, m4
    shr             t1, 11
    add           lpfq, wq
    movd            m2, [base+wiener_round+t1*4]
    movq            m3, [base+wiener_shifts+t1*8]
 %if STACK_ALIGNMENT < 16
    lea             t1, [esp+16*11+wq+16]
 %else
    lea             t1, [esp+16*10+wq+16]
 %endif
    add           dstq, wq
    neg             wq
    pshufd          m2, m2, q0000
    pshufd          m4, m3, q0000
    pshufd          m3, m3, q1111
    mov             wm, wq
    pmullw          m0, m4
    pmullw          m1, m4
    mova            m4, [base+wiener_lshuf5]
    mova            m9, m2
    mova           m10, m3
    mova           m11, m0
    mova           m12, m1
    mova           m15, m4
%endif
    test         edgeb, 4 ; LR_HAVE_TOP
    jz .no_top
    call .h_top
    add           lpfq, strideq
    mov             t4, t1
    add             t1, 384*2
    call .h_top
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    mov             t3, t1
    add             t1, 384*2
    add            r10, strideq
    mov           lpfm, r10 ; below
    call .h
    mov             t2, t1
    dec             hd
    jz .v1
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    dec             hd
    jz .v2
.main:
    mov             t0, t4
.main_loop:
    call .hv
    dec             hd
    jnz .main_loop
    test         edgeb, 8 ; LR_HAVE_BOTTOM
    jz .v2
    mov           lpfq, lpfm
    call .hv_bottom
    add           lpfq, strideq
    call .hv_bottom
.end:
    RET
.no_top:
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    lea            r10, [r10+strideq*2]
    mov           lpfm, r10
    call .h
    mov             t4, t1
    mov             t3, t1
    mov             t2, t1
    dec             hd
    jz .v1
    add           lpfq, strideq
    add             t1, 384*2
    call .h
    dec             hd
    jz .v2
    lea             t0, [t1+384*2]
    call .hv
    dec             hd
    jz .v2
    add             t0, 384*6
    call .hv
    dec             hd
    jnz .main
.v2:
    call .v
%if ARCH_X86_64
    mov             t4, t3
    mov             t3, t2
    mov             t2, t1
%else
    mov             t0, t3m
    mov             r4, t2m
    mov             t1, t1m
    mov            t4m, t0
    mov            t3m, r4
    mov            t2m, t1
    mov             wq, wm
%endif
    add           dstq, strideq
.v1:
    call .v
    jmp .end
.extend_right:
%assign stack_offset stack_offset+8
%assign calloff 8
    movif32         t0, PICmem
    pxor            m1, m1
    movd            m2, wd
    mova            m0, [base+pb_2_3]
    pshufb          m2, m1
    mova            m1, [base+pb_m6_m5]
    psubb           m0, m2
    psubb           m1, m2
    mova            m2, [base+pb_0to15]
    pminub          m0, m2
    pminub          m1, m2
    pshufb          m3, m0
    pshufb          m4, m1
    ret
%assign stack_offset stack_offset-4
%assign calloff 4
.h:
    movif64         wq, r4
    movif32         wq, wm
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    mova            m4, [lpfq+wq]
    movd            m3, [leftq+4]
    pslldq          m4, 4
    por             m3, m4
    add          leftq, 8
    jmp .h_main
.h_extend_left:
    mova            m3, [lpfq+wq] ; avoid accessing memory located
    pshufb          m3, m15       ; before the start of the buffer
    jmp .h_main
.h_top:
    movif64         wq, r4
    movif32         wq, wm
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
.h_loop:
    movu            m3, [lpfq+wq-4]
.h_main:
    movu            m4, [lpfq+wq+4]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .h_have_right
    cmp             wd, -18
    jl .h_have_right
    call .extend_right
.h_have_right:
    pshufb          m0, m3, m5
    pmaddwd         m0, m11
    pshufb          m1, m4, m5
    pmaddwd         m1, m11
    pshufb          m2, m3, m6
    pshufb          m3, m7
    paddw           m2, m3
    pshufb          m3, m4, m6
    pmaddwd         m2, m12
    pshufb          m4, m7
    paddw           m3, m4
    pmaddwd         m3, m12
    paddd           m0, m8
    paddd           m1, m8
    paddd           m0, m2
    paddd           m1, m3
    psrad           m0, 4
    psrad           m1, 4
    packssdw        m0, m1
    psraw           m0, 1
    mova       [t1+wq], m0
    add             wq, 16
    jl .h_loop
    movif32         wq, wm
    ret
ALIGN function_align
.hv:
    add           lpfq, strideq
    movif64         wq, r4
    movif32        t0m, t0
    movif32        t1m, t1
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
    mova            m4, [lpfq+wq]
    movd            m3, [leftq+4]
    pslldq          m4, 4
    por             m3, m4
    add          leftq, 8
    jmp .hv_main
.hv_extend_left:
    mova            m3, [lpfq+wq]
    pshufb          m3, m15
    jmp .hv_main
.hv_bottom:
    movif64         wq, r4
    movif32        t0m, t0
    movif32        t1m, t1
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
.hv_loop:
    movu            m3, [lpfq+wq-4]
.hv_main:
    movu            m4, [lpfq+wq+4]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv_have_right
    cmp             wd, -18
    jl .hv_have_right
    call .extend_right
.hv_have_right:
    movif32         t1, t1m
    movif32         t0, t3m
    pshufb          m0, m3, m5
    pmaddwd         m0, m11
    pshufb          m1, m4, m5
    pmaddwd         m1, m11
    pshufb          m2, m3, m6
    pshufb          m3, m7
    paddw           m2, m3
    pshufb          m3, m4, m6
    pmaddwd         m2, m12
    pshufb          m4, m7
    paddw           m3, m4
    pmaddwd         m3, m12
    paddd           m0, m8
    paddd           m1, m8
    paddd           m0, m2
%if ARCH_X86_64
    mova            m2, [t3+wq]
    paddw           m2, [t1+wq]
    paddd           m1, m3
    mova            m4, [t2+wq]
%else
    mova            m2, [t0+wq]
    mov             t0, t2m
    paddw           m2, [t1+wq]
    mov             t1, t4m
    paddd           m1, m3
    mova            m4, [t0+wq]
    mov             t0, t0m
%endif
    punpckhwd       m3, m2, m4
    pmaddwd         m3, m14
    punpcklwd       m2, m4
%if ARCH_X86_64
    mova            m4, [t4+wq]
%else
    mova            m4, [t1+wq]
%endif
    psrad           m0, 4
    psrad           m1, 4
    packssdw        m0, m1
    pmaddwd         m2, m14
    psraw           m0, 1
    mova       [t0+wq], m0
    punpckhwd       m1, m0, m4
    pmaddwd         m1, m13
    punpcklwd       m0, m4
    pmaddwd         m0, m13
    paddd           m3, m9
    paddd           m2, m9
    paddd           m1, m3
    paddd           m0, m2
    psrad           m1, 6
    psrad           m0, 6
    packssdw        m0, m1
    pmulhw          m0, m10
    pxor            m1, m1
    pmaxsw          m0, m1
    mova     [dstq+wq], m0
    add             wq, 16
    jl .hv_loop
%if ARCH_X86_64
    mov             t4, t3
    mov             t3, t2
    mov             t2, t1
    mov             t1, t0
    mov             t0, t4
%else
    mov             r4, t3m
    mov             t1, t2m
    mov            t4m, r4
    mov            t3m, t1
    mov             r4, t1m
    mov             t1, t0
    mov            t2m, r4
    mov             t0, t4m
    mov             wq, wm
%endif
    add           dstq, strideq
    ret
.v:
    movif64         wq, r4
    movif32        t1m, t1
.v_loop:
%if ARCH_X86_64
    mova            m0, [t1+wq]
    paddw           m2, m0, [t3+wq]
    mova            m1, [t2+wq]
    mova            m4, [t4+wq]
%else
    mov             t0, t3m
    mova            m0, [t1+wq]
    mov             t1, t2m
    paddw           m2, m0, [t0+wq]
    mov             t0, t4m
    mova            m1, [t1+wq]
    mova            m4, [t0+wq]
%endif
    punpckhwd       m3, m2, m1
    pmaddwd         m3, m14
    punpcklwd       m2, m1
    pmaddwd         m2, m14
    punpckhwd       m1, m0, m4
    pmaddwd         m1, m13
    punpcklwd       m0, m4
    pmaddwd         m0, m13
    paddd           m3, m9
    paddd           m2, m9
    paddd           m1, m3
    paddd           m0, m2
    psrad           m1, 6
    psrad           m0, 6
    packssdw        m0, m1
    pmulhw          m0, m10
    pxor            m1, m1
    pmaxsw          m0, m1
    mova     [dstq+wq], m0
    add             wq, 16
%if ARCH_X86_64
    jl .v_loop
%else
    jge .v_end
    mov             t1, t1m
    jmp .v_loop
.v_end:
%endif
    ret

%macro MUL_32X16X2 6 ; dst[1-2], src[1-2], tmp[1-2]
    pmulhuw         %5, %1, %3
    pmulhuw         %6, %2, %4
    pmullw          %1, %3
    pmullw          %2, %4
    pslld           %5, 16
    pslld           %6, 16
    paddd           %1, %5
    paddd           %2, %6
%endmacro

%macro SGR_CALC_X 10 ; BB_dst, BB_src, b, tmp, an[1-2], zero, s, b_mul, pf_256
    punpcklwd       %4, %3, %7
    punpckhwd       %3, %7
    pmaddwd         %4, %4                 ; b * b
    pmaddwd         %3, %3
    punpcklwd       %1, %2, %7             ; BB
    punpckhwd       %2, %7
    psubd           %5, %4                 ; a * n - b * b
    psubd           %6, %3
    pcmpgtd         %4, %5, %7
    pcmpgtd         %3, %6, %7
    pand            %5, %4                 ; p
    pand            %6, %3
    MUL_32X16X2     %5, %6, %8, %8, %4, %3 ; p * s
    paddw           %5, %9
    paddw           %6, %9
    psrld           %5, 20                 ; z + 1
    psrld           %6, 20
    cvtdq2ps        %5, %5
    cvtdq2ps        %6, %6
    pmaddwd         %1, %9                 ; BB * 164
    pmaddwd         %2, %9
    rcpps           %3, %5                 ; 1 / (z + 1)
    rcpps           %4, %6
    cmpltps         %5, %10
    cmpltps         %6, %10
    mulps           %3, %10                ; 256 / (z + 1)
    mulps           %4, %10
    packssdw        %5, %6
    cvtps2dq        %3, %3
    cvtps2dq        %4, %4
    psrlw           %5, 8                  ; z < 255 ? 255 : 0
    packssdw        %3, %4
    pminsw          %3, %5                 ; x
%endmacro

%if ARCH_X86_32
DECLARE_REG_TMP 0, 1, 2, 3, 5
 %if STACK_ALIGNMENT < 16
  %assign extra_stack 5*16
 %else
  %assign extra_stack 3*16
 %endif
cglobal sgr_filter_5x5_16bpc, 1, 7, 8, -400*24-16-extra_stack, \
                              dst, stride, left, lpf, w
 %if STACK_ALIGNMENT < 16
  %define dstm         dword [esp+calloff+16*0+4*6]
  %define stridemp     dword [esp+calloff+16*0+4*7]
  %define leftm        dword [esp+calloff+16*3+4*0]
  %define lpfm         dword [esp+calloff+16*3+4*1]
  %define w0m          dword [esp+calloff+16*3+4*2]
  %define hd           dword [esp+calloff+16*3+4*3]
  %define edgeb         byte [esp+calloff+16*3+4*4]
  %define edged        dword [esp+calloff+16*3+4*4]
  %define leftmp leftm
 %else
  %define w0m wm
  %define hd dword r5m
  %define edgeb  byte r7m
  %define edged dword r7m
 %endif
 %define hvsrcm dword [esp+calloff+4*0]
 %define w1m    dword [esp+calloff+4*1]
 %define t0m    dword [esp+calloff+4*2]
 %define t2m    dword [esp+calloff+4*3]
 %define t3m    dword [esp+calloff+4*4]
 %define t4m    dword [esp+calloff+4*5]
 %define  m8 [base+pd_8]
 %define  m9 [base+pd_0xfffffff0]
 %define m10 [esp+calloff+16*2]
 %define m11 [base+pw_164_24]
 %define m12 [base+sgr_lshuf5]
 %define m13 [base+pd_34816]
 %define m14 [base+pw_1023]
 %define m15 [base+pf_256]
 %define r10 r4
 %define base r6-pw_455_24
 %assign calloff 0
 %if STACK_ALIGNMENT < 16
    mov        strideq, [rstk+stack_offset+ 8]
    mov          leftq, [rstk+stack_offset+12]
    mov           lpfq, [rstk+stack_offset+16]
    mov             wd, [rstk+stack_offset+20]
    mov           dstm, dstq
    mov       stridemp, strideq
    mov          leftm, leftq
    mov             r1, [rstk+stack_offset+24]
    mov             r2, [rstk+stack_offset+32]
    mov           lpfm, lpfq
    mov             hd, r1
    mov          edged, r2
 %endif
%else
cglobal sgr_filter_5x5_16bpc, 4, 13, 16, -400*24-16, dst, stride, left, lpf, \
                                                     w, h, edge, params
%endif
%if ARCH_X86_64 || STACK_ALIGNMENT >= 16
    movifnidn       wd, wm
%endif
%if ARCH_X86_64
    mov        paramsq, r6mp
    movifnidn       hd, hm
    add             wd, wd
    mov          edged, r7m
    movu           m10, [paramsq]
    mova           m12, [sgr_lshuf5]
    add           lpfq, wq
    mova            m8, [pd_8]
    lea             t1, [rsp+wq+20]
    mova            m9, [pd_0xfffffff0]
    add           dstq, wq
    lea             t3, [rsp+wq*2+400*12+16]
    mova           m11, [pw_164_24]
    lea             t4, [rsp+wq+400*20+16]
    pshufhw         m7, m10, q0000
    pshufb         m10, [pw_256]  ; s0
    punpckhqdq      m7, m7        ; w0
    neg             wq
    mova           m13, [pd_34816]  ; (1 << 11) + (1 << 15)
    pxor            m6, m6
    mova           m14, [pw_1023]
    psllw           m7, 4
    movaps         m15, [pf_256]
 DEFINE_ARGS dst, stride, left, lpf, _, h, edge, _, _, _, w
 %define lpfm        [rsp]
%else
    mov             r1, [rstk+stack_offset+28] ; params
    LEA             r6, pw_455_24
    add             wd, wd
    movu            m1, [r1]
    add           lpfm, wq
    lea             t1, [rsp+extra_stack+wq+20]
    add           dstq, wq
    lea             t3, [rsp+extra_stack+wq*2+400*12+16]
    mov           dstm, dstq
    lea             t4, [rsp+extra_stack+wq+400*20+16]
    mov            t3m, t3
    pshufhw         m7, m1, q0000
    mov            t4m, t4
    pshufb          m1, [base+pw_256] ; s0
    punpckhqdq      m7, m7            ; w0
    psllw           m7, 4
    neg             wq
    mova           m10, m1
    pxor            m6, m6
    mov            w1m, wd
    sub             wd, 4
    mov           lpfq, lpfm
    mov            w0m, wd
 %define strideq r5
%endif
    test         edgeb, 4 ; LR_HAVE_TOP
    jz .no_top
    call .h_top
    add           lpfq, stridemp
    mov             t2, t1
    call .top_fixup
    add             t1, 400*6
    call .h_top
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    add            r10, strideq
    mov           lpfm, r10 ; below
    movif32        t0m, t2
    mov             t0, t2
    dec             hd
    jz .height1
    or           edged, 16
    call .h
.main:
    add           lpfq, stridemp
    movif32         t4, t4m
    call .hv
    call .prep_n
    sub             hd, 2
    jl .extend_bottom
.main_loop:
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
%if ARCH_X86_64
    test            hb, hb
%else
    mov             r4, hd
    test            r4, r4
%endif
    jz .odd_height
    call .h
    add           lpfq, stridemp
    call .hv
    movif32       dstq, dstm
    call .n0
    call .n1
    sub             hd, 2
    movif32         t0, t0m
    jge .main_loop
    test         edgeb, 8 ; LR_HAVE_BOTTOM
    jz .extend_bottom
    mov           lpfq, lpfm
    call .h_top
    add           lpfq, stridemp
    call .hv_bottom
.end:
    movif32       dstq, dstm
    call .n0
    call .n1
.end2:
    RET
.height1:
    movif32         t4, t4m
    call .hv
    call .prep_n
    jmp .odd_height_end
.odd_height:
    call .hv
    movif32       dstq, dstm
    call .n0
    call .n1
.odd_height_end:
    call .v
    movif32       dstq, dstm
    call .n0
    jmp .end2
.extend_bottom:
    call .v
    jmp .end
.no_top:
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    lea            r10, [r10+strideq*2]
    mov           lpfm, r10
    call .h
    lea             t2, [t1+400*6]
    call .top_fixup
    dec             hd
    jz .no_top_height1
    or           edged, 16
    mov             t0, t1
    mov             t1, t2
    movif32        t0m, t0
    jmp .main
.no_top_height1:
    movif32         t3, t3m
    movif32         t4, t4m
    call .v
    call .prep_n
    jmp .odd_height_end
.extend_right:
    movd            m0, wd
    movd            m1, [lpfq-2]
    mova            m2, [base+pw_256]
    mova            m3, [base+pb_m14_m13]
    pshufb          m0, m6
    pshufb          m1, m2
    psubb           m2, m0
    psubb           m3, m0
    mova            m0, [base+pb_0to15]
    pcmpgtb         m2, m0
    pcmpgtb         m3, m0
    pand            m4, m2
    pand            m5, m3
    pandn           m2, m1
    pandn           m3, m1
    por             m4, m2
    por             m5, m3
    ret
%assign stack_offset stack_offset+4
%assign calloff 4
.h: ; horizontal boxsum
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
 %define leftq r4
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 10
    jmp .h_main
.h_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, m12
    jmp .h_main
.h_top:
%if ARCH_X86_64
    lea             wq, [r4-4]
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32         wq, w0m
.h_loop:
    movu            m4, [lpfq+wq- 2]
.h_main:
    movu            m5, [lpfq+wq+14]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .h_have_right
    cmp             wd, -20
    jl .h_have_right
    call .extend_right
.h_have_right:
    palignr         m2, m5, m4, 2
    paddw           m0, m4, m2
    palignr         m3, m5, m4, 6
    paddw           m0, m3
    punpcklwd       m1, m2, m3
    pmaddwd         m1, m1
    punpckhwd       m2, m3
    pmaddwd         m2, m2
    palignr         m5, m4, 8
    paddw           m0, m5
    punpcklwd       m3, m4, m5
    pmaddwd         m3, m3
    paddd           m1, m3
    punpckhwd       m3, m4, m5
    pmaddwd         m3, m3
    shufps          m4, m5, q2121
    paddw           m0, m4             ; sum
    punpcklwd       m5, m4, m6
    pmaddwd         m5, m5
    punpckhwd       m4, m6
    pmaddwd         m4, m4
    paddd           m2, m3
    test         edgeb, 16             ; y > 0
    jz .h_loop_end
    paddw           m0, [t1+wq+400*0]
    paddd           m1, [t1+wq+400*2]
    paddd           m2, [t1+wq+400*4]
.h_loop_end:
    paddd           m1, m5             ; sumsq
    paddd           m2, m4
    mova [t1+wq+400*0], m0
    mova [t1+wq+400*2], m1
    mova [t1+wq+400*4], m2
    add             wq, 16
    jl .h_loop
    ret
.top_fixup:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
.top_fixup_loop: ; the sums of the first row needs to be doubled
    mova            m0, [t1+wq+400*0]
    mova            m1, [t1+wq+400*2]
    mova            m2, [t1+wq+400*4]
    paddw           m0, m0
    paddd           m1, m1
    paddd           m2, m2
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m1
    mova [t2+wq+400*4], m2
    add             wq, 16
    jl .top_fixup_loop
    ret
ALIGN function_align
.hv: ; horizontal boxsum + vertical boxsum + ab
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 10
    jmp .hv_main
.hv_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, m12
    jmp .hv_main
.hv_bottom:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv_extend_left
    movif32         wq, w0m
%if ARCH_X86_32
    jmp .hv_loop_start
%endif
.hv_loop:
    movif32       lpfq, hvsrcm
.hv_loop_start:
    movu            m4, [lpfq+wq- 2]
.hv_main:
    movu            m5, [lpfq+wq+14]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv_have_right
    cmp             wd, -20
    jl .hv_have_right
    call .extend_right
.hv_have_right:
    movif32         t3, hd
    palignr         m3, m5, m4, 2
    paddw           m0, m4, m3
    palignr         m1, m5, m4, 6
    paddw           m0, m1
    punpcklwd       m2, m3, m1
    pmaddwd         m2, m2
    punpckhwd       m3, m1
    pmaddwd         m3, m3
    palignr         m5, m4, 8
    paddw           m0, m5
    punpcklwd       m1, m4, m5
    pmaddwd         m1, m1
    paddd           m2, m1
    punpckhwd       m1, m4, m5
    pmaddwd         m1, m1
    shufps          m4, m5, q2121
    paddw           m0, m4            ; h sum
    punpcklwd       m5, m4, m6
    pmaddwd         m5, m5
    punpckhwd       m4, m6
    pmaddwd         m4, m4
    paddd           m3, m1
    paddd           m2, m5            ; h sumsq
    paddd           m3, m4
    paddw           m1, m0, [t1+wq+400*0]
    paddd           m4, m2, [t1+wq+400*2]
    paddd           m5, m3, [t1+wq+400*4]
%if ARCH_X86_64
    test            hd, hd
%else
    test            t3, t3
%endif
    jz .hv_last_row
.hv_main2:
    paddw           m1, [t2+wq+400*0] ; hv sum
    paddd           m4, [t2+wq+400*2] ; hv sumsq
    paddd           m5, [t2+wq+400*4]
    mova [t0+wq+400*0], m0
    mova [t0+wq+400*2], m2
    mova [t0+wq+400*4], m3
    psrlw           m3, m1, 1
    paddd           m4, m8
    pavgw           m3, m6             ; (b + 2) >> 2
    paddd           m5, m8
    pand            m4, m9             ; ((a + 8) >> 4) << 4
    pand            m5, m9
    psrld           m2, m4, 4
    psrld           m0, m5, 4
    paddd           m2, m4
    psrld           m4, 1
    paddd           m0, m5
    psrld           m5, 1
    paddd           m4, m2             ; a * 25
    paddd           m5, m0
    movif32         t3, t3m
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m10, m11, m15
    punpcklwd       m2, m3, m3
    mova     [t4+wq+4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m13            ; x * b * 164 + (1 << 11) + (1 << 15)
    paddd           m1, m13
    psrld           m0, 12             ; b
    psrld           m1, 12
    mova  [t3+wq*2+ 8], m0
    mova  [t3+wq*2+24], m1
    add             wq, 16
    jl .hv_loop
    mov             t2, t1
    mov             t1, t0
    mov             t0, t2
    movif32        t0m, t0
    ret
.hv_last_row: ; esoteric edge case for odd heights
    mova [t1+wq+400*0], m1
    paddw           m1, m0
    mova [t1+wq+400*2], m4
    paddd           m4, m2
    mova [t1+wq+400*4], m5
    paddd           m5, m3
    jmp .hv_main2
.v: ; vertical boxsum + ab
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
.v_loop:
    mova            m0, [t1+wq+400*0]
    mova            m2, [t1+wq+400*2]
    mova            m3, [t1+wq+400*4]
    paddw           m1, m0, [t2+wq+400*0]
    paddd           m4, m2, [t2+wq+400*2]
    paddd           m5, m3, [t2+wq+400*4]
    paddw           m0, m0
    paddd           m2, m2
    paddd           m3, m3
    paddw           m1, m0             ; hv sum
    paddd           m4, m2             ; hv sumsq
    paddd           m5, m3
    psrlw           m3, m1, 1
    paddd           m4, m8
    pavgw           m3, m6             ; (b + 2) >> 2
    paddd           m5, m8
    pand            m4, m9             ; ((a + 8) >> 4) << 4
    pand            m5, m9
    psrld           m2, m4, 4
    psrld           m0, m5, 4
    paddd           m2, m4
    psrld           m4, 1
    paddd           m0, m5
    psrld           m5, 1
    paddd           m4, m2             ; a * 25
    paddd           m5, m0
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m10, m11, m15
    punpcklwd       m2, m3, m3
    mova     [t4+wq+4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m13            ; x * b * 164 + (1 << 11) + (1 << 15)
    paddd           m1, m13
    psrld           m0, 12             ; b
    psrld           m1, 12
    mova  [t3+wq*2+ 8], m0
    mova  [t3+wq*2+24], m1
    add             wq, 16
    jl .v_loop
    ret
.prep_n: ; initial neighbor setup
    movif64         wq, r4
    movif32         wd, w1m
.prep_n_loop:
    movu            m0, [t4+wq*1+ 2]
    movu            m3, [t4+wq*1+ 4]
    movu            m1, [t3+wq*2+ 4]
    movu            m4, [t3+wq*2+ 8]
    movu            m2, [t3+wq*2+20]
    movu            m5, [t3+wq*2+24]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    paddw           m3, [t4+wq*1+ 0]
    paddd           m4, [t3+wq*2+ 0]
    paddd           m5, [t3+wq*2+16]
    paddw           m0, m3
    psllw           m3, 2
    paddd           m1, m4
    pslld           m4, 2
    paddd           m2, m5
    pslld           m5, 2
    paddw           m0, m3             ; a 565
    paddd           m1, m4             ; b 565
    paddd           m2, m5
    mova [t4+wq*1+400*2+ 0], m0
    mova [t3+wq*2+400*4+ 0], m1
    mova [t3+wq*2+400*4+16], m2
    add             wq, 16
    jl .prep_n_loop
    ret
ALIGN function_align
.n0: ; neighbor + output (even rows)
    movif64         wq, r4
    movif32         wd, w1m
.n0_loop:
    movu            m0, [t4+wq*1+ 2]
    movu            m3, [t4+wq*1+ 4]
    movu            m1, [t3+wq*2+ 4]
    movu            m4, [t3+wq*2+ 8]
    movu            m2, [t3+wq*2+20]
    movu            m5, [t3+wq*2+24]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    paddw           m3, [t4+wq*1+ 0]
    paddd           m4, [t3+wq*2+ 0]
    paddd           m5, [t3+wq*2+16]
    paddw           m0, m3
    psllw           m3, 2
    paddd           m1, m4
    pslld           m4, 2
    paddd           m2, m5
    pslld           m5, 2
    paddw           m0, m3             ; a 565
    paddd           m1, m4             ; b 565
    paddd           m2, m5
    paddw           m3, m0, [t4+wq*1+400*2+ 0]
    paddd           m4, m1, [t3+wq*2+400*4+ 0]
    paddd           m5, m2, [t3+wq*2+400*4+16]
    mova [t4+wq*1+400*2+ 0], m0
    mova [t3+wq*2+400*4+ 0], m1
    mova [t3+wq*2+400*4+16], m2
    mova            m0, [dstq+wq]
    punpcklwd       m1, m0, m6          ; src
    punpcklwd       m2, m3, m6          ; a
    pmaddwd         m2, m1              ; a * src
    punpckhwd       m1, m0, m6
    punpckhwd       m3, m6
    pmaddwd         m3, m1
    psubd           m4, m2              ; b - a * src + (1 << 8)
    psubd           m5, m3
    psrad           m4, 9
    psrad           m5, 9
    packssdw        m4, m5
    pmulhrsw        m4, m7
    paddw           m0, m4
    pmaxsw          m0, m6
    pminsw          m0, m14
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n0_loop
    add           dstq, stridemp
    ret
ALIGN function_align
.n1: ; neighbor + output (odd rows)
    movif64         wq, r4
    movif32         wd, w1m
.n1_loop:
    mova            m0, [dstq+wq]
    mova            m3, [t4+wq*1+400*2+ 0]
    mova            m4, [t3+wq*2+400*4+ 0]
    mova            m5, [t3+wq*2+400*4+16]
    punpcklwd       m1, m0, m6          ; src
    punpcklwd       m2, m3, m6          ; a
    pmaddwd         m2, m1
    punpckhwd       m1, m0, m6
    punpckhwd       m3, m6
    pmaddwd         m3, m1
    psubd           m4, m2              ; b - a * src + (1 << 7)
    psubd           m5, m3
    psrad           m4, 8
    psrad           m5, 8
    packssdw        m4, m5
    pmulhrsw        m4, m7
    paddw           m0, m4
    pmaxsw          m0, m6
    pminsw          m0, m14
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n1_loop
    add           dstq, stridemp
    movif32       dstm, dstq
    ret

%if ARCH_X86_32
 %if STACK_ALIGNMENT < 16
  %assign extra_stack 4*16
 %else
  %assign extra_stack 2*16
 %endif
cglobal sgr_filter_3x3_16bpc, 1, 7, 8, -400*42-16-extra_stack, \
                              dst, stride, left, lpf, w
 %if STACK_ALIGNMENT < 16
  %define dstm         dword [esp+calloff+16*2+4*0]
  %define stridemp     dword [esp+calloff+16*2+4*1]
  %define leftm        dword [esp+calloff+16*2+4*2]
  %define lpfm         dword [esp+calloff+16*2+4*3]
  %define w0m          dword [esp+calloff+16*2+4*4]
  %define hd           dword [esp+calloff+16*2+4*5]
  %define edgeb         byte [esp+calloff+16*2+4*6]
  %define edged        dword [esp+calloff+16*2+4*6]
  %define leftmp leftm
 %else
  %define w0m wm
  %define hd dword r5m
  %define edgeb  byte r7m
  %define edged dword r7m
 %endif
 %define hvsrcm dword [esp+calloff+4*0]
 %define w1m    dword [esp+calloff+4*1]
 %define t3m    dword [esp+calloff+4*2]
 %define t4m    dword [esp+calloff+4*3]
 %define  m8 [base+pd_8]
 %define  m9 [esp+calloff+16*1]
 %define m10 [base+pw_455_24]
 %define m11 [base+pd_34816]
 %define m12 [base+sgr_lshuf3]
 %define m13 [base+pw_1023]
 %define m14 [base+pf_256]
 %define base r6-pw_455_24
 %assign calloff 0
 %if STACK_ALIGNMENT < 16
    mov        strideq, [rstk+stack_offset+ 8]
    mov          leftq, [rstk+stack_offset+12]
    mov           lpfq, [rstk+stack_offset+16]
    mov             wd, [rstk+stack_offset+20]
    mov           dstm, dstq
    mov       stridemp, strideq
    mov          leftm, leftq
    mov             r1, [rstk+stack_offset+24]
    mov             r2, [rstk+stack_offset+32]
    mov           lpfm, lpfq
    mov             hd, r1
    mov          edged, r2
 %endif
%else
cglobal sgr_filter_3x3_16bpc, 4, 13, 15, -400*42-8, dst, stride, left, lpf, \
                                                    w, h, edge, params
%endif
%if ARCH_X86_64 || STACK_ALIGNMENT >= 16
    movifnidn       wd, wm
%endif
%if ARCH_X86_64
    mov        paramsq, r6mp
    movifnidn       hd, hm
    add             wd, wd
    mov          edged, r7m
    movq            m9, [paramsq+4]
    add           lpfq, wq
    lea             t1, [rsp+wq+12]
    mova            m8, [pd_8]
    add           dstq, wq
    lea             t3, [rsp+wq*2+400*12+8]
    mova           m10, [pw_455_24]
    lea             t4, [rsp+wq+400*32+8]
    mova           m11, [pd_34816]
    pshuflw         m7, m9, q3333
    pshufb          m9, [pw_256]  ; s1
    punpcklqdq      m7, m7        ; w1
    neg             wq
    pxor            m6, m6
    mova           m13, [pw_1023]
    psllw           m7, 4
    mova           m12, [sgr_lshuf3]
    movaps         m14, [pf_256]
 DEFINE_ARGS dst, stride, left, lpf, _, h, edge, _, _, _, w
 %define lpfm [rsp]
%else
    mov             r1, [rstk+stack_offset+28] ; params
    LEA             r6, pw_455_24
    add             wd, wd
    movq            m1, [r1+4]
    add           lpfm, wq
    lea             t1, [rsp+extra_stack+wq+20]
    add           dstq, wq
    lea             t3, [rsp+extra_stack+wq*2+400*12+16]
    mov           dstm, dstq
    lea             t4, [rsp+extra_stack+wq+400*32+16]
    mov            t3m, t3
    pshuflw         m7, m1, q3333
    mov            t4m, t4
    pshufb          m1, [base+pw_256] ; s1
    punpcklqdq      m7, m7            ; w1
    psllw           m7, 4
    neg             wq
    mova            m9, m1
    pxor            m6, m6
    mov            w1m, wd
    sub             wd, 4
    mov           lpfq, lpfm
    mov            w0m, wd
 %define strideq r5
%endif
    test         edgeb, 4 ; LR_HAVE_TOP
    jz .no_top
    call .h_top
    add           lpfq, stridemp
    mov             t2, t1
    add             t1, 400*6
    call .h_top
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    add            r10, strideq
    mov           lpfm, r10 ; below
    movif32         t4, t4m
    call .hv0
.main:
    dec             hd
    jz .height1
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1
    call .prep_n
    sub             hd, 2
    jl .extend_bottom
.main_loop:
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv0
%if ARCH_X86_64
    test            hb, hb
%else
    mov             r4, hd
    test            r4, r4
%endif
    jz .odd_height
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1
    call .n0
    call .n1
    sub             hd, 2
    jge .main_loop
    test         edgeb, 8 ; LR_HAVE_BOTTOM
    jz .extend_bottom
    mov           lpfq, lpfm
    call .hv0_bottom
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1_bottom
.end:
    call .n0
    call .n1
.end2:
    RET
.height1:
    call .v1
    call .prep_n
    jmp .odd_height_end
.odd_height:
    call .v1
    call .n0
    call .n1
.odd_height_end:
    call .v0
    call .v1
    call .n0
    jmp .end2
.extend_bottom:
    call .v0
    call .v1
    jmp .end
.no_top:
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    lea            r10, [r10+strideq*2]
    mov           lpfm, r10
    call .h
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wq, w0m
    mov         hvsrcm, lpfq
%endif
    lea             t2, [t1+400*6]
.top_fixup_loop:
    mova            m0, [t1+wq+400*0]
    mova            m1, [t1+wq+400*2]
    mova            m2, [t1+wq+400*4]
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m1
    mova [t2+wq+400*4], m2
    add             wq, 16
    jl .top_fixup_loop
    movif32         t3, t3m
    movif32         t4, t4m
    call .v0
    jmp .main
.extend_right:
    movd            m1, wd
    movd            m5, [lpfq-2]
    mova            m2, [base+pw_256]
    mova            m3, [base+pb_0to15]
    pshufb          m1, m6
    pshufb          m5, m2
    psubb           m2, m1
    pcmpgtb         m2, m3
    pand            m4, m2
    pandn           m2, m5
    por             m4, m2
    ret
%assign stack_offset stack_offset+4
%assign calloff 4
.h: ; horizontal boxsum
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
 %define leftq r4
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 12
    jmp .h_main
.h_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, m12
    jmp .h_main
.h_top:
%if ARCH_X86_64
    lea             wq, [r4-4]
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32         wq, w0m
.h_loop:
    movu            m4, [lpfq+wq+ 0]
.h_main:
    movu            m5, [lpfq+wq+16]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .h_have_right
    cmp             wd, -18
    jl .h_have_right
    call .extend_right
.h_have_right:
    palignr         m0, m5, m4, 2
    paddw           m1, m4, m0
    punpcklwd       m2, m4, m0
    pmaddwd         m2, m2
    punpckhwd       m3, m4, m0
    pmaddwd         m3, m3
    palignr         m5, m4, 4
    paddw           m1, m5             ; sum
    punpcklwd       m4, m5, m6
    pmaddwd         m4, m4
    punpckhwd       m5, m6
    pmaddwd         m5, m5
    paddd           m2, m4             ; sumsq
    paddd           m3, m5
    mova [t1+wq+400*0], m1
    mova [t1+wq+400*2], m2
    mova [t1+wq+400*4], m3
    add             wq, 16
    jl .h_loop
    ret
ALIGN function_align
.hv0: ; horizontal boxsum + vertical boxsum + ab (even rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv0_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 12
    jmp .hv0_main
.hv0_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, m12
    jmp .hv0_main
.hv0_bottom:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv0_extend_left
    movif32         wq, w0m
%if ARCH_X86_32
    jmp .hv0_loop_start
%endif
.hv0_loop:
    movif32       lpfq, hvsrcm
.hv0_loop_start:
    movu            m4, [lpfq+wq+ 0]
.hv0_main:
    movu            m5, [lpfq+wq+16]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv0_have_right
    cmp             wd, -18
    jl .hv0_have_right
    call .extend_right
.hv0_have_right:
    palignr         m0, m5, m4, 2
    paddw           m1, m4, m0
    punpcklwd       m2, m4, m0
    pmaddwd         m2, m2
    punpckhwd       m3, m4, m0
    pmaddwd         m3, m3
    palignr         m5, m4, 4
    paddw           m1, m5             ; sum
    punpcklwd       m4, m5, m6
    pmaddwd         m4, m4
    punpckhwd       m5, m6
    pmaddwd         m5, m5
    paddd           m2, m4             ; sumsq
    paddd           m3, m5
    paddw           m0, m1, [t1+wq+400*0]
    paddd           m4, m2, [t1+wq+400*2]
    paddd           m5, m3, [t1+wq+400*4]
    mova [t1+wq+400*0], m1
    mova [t1+wq+400*2], m2
    mova [t1+wq+400*4], m3
    paddw           m1, m0, [t2+wq+400*0]
    paddd           m2, m4, [t2+wq+400*2]
    paddd           m3, m5, [t2+wq+400*4]
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m4
    mova [t2+wq+400*4], m5
    paddd           m2, m8
    paddd           m3, m8
    psrld           m2, 4              ; (a + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
    pavgw           m3, m6             ; (b + 2) >> 2
    movif32         t3, t3m
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m9, m10, m14
    punpcklwd       m2, m3, m3
    mova     [t4+wq+4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m11            ; x * b * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m11
    psrld           m0, 12
    psrld           m1, 12
    mova  [t3+wq*2+ 8], m0
    mova  [t3+wq*2+24], m1
    add             wq, 16
    jl .hv0_loop
    ret
ALIGN function_align
.hv1: ; horizontal boxsums + vertical boxsums + ab (odd rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv1_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 12
    jmp .hv1_main
.hv1_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, m12
    jmp .hv1_main
.hv1_bottom:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv1_extend_left
    movif32         wq, w0m
%if ARCH_X86_32
    jmp .hv1_loop_start
%endif
.hv1_loop:
    movif32       lpfq, hvsrcm
.hv1_loop_start:
    movu            m4, [lpfq+wq+ 0]
.hv1_main:
    movu            m5, [lpfq+wq+16]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv1_have_right
    cmp             wd, -18
    jl .hv1_have_right
    call .extend_right
.hv1_have_right:
    palignr         m1, m5, m4, 2
    paddw           m0, m4, m1
    punpcklwd       m2, m4, m1
    pmaddwd         m2, m2
    punpckhwd       m3, m4, m1
    pmaddwd         m3, m3
    palignr         m5, m4, 4
    paddw           m0, m5             ; h sum
    punpcklwd       m1, m5, m6
    pmaddwd         m1, m1
    punpckhwd       m5, m6
    pmaddwd         m5, m5
    paddd           m2, m1             ; h sumsq
    paddd           m3, m5
    paddw           m1, m0, [t2+wq+400*0]
    paddd           m4, m2, [t2+wq+400*2]
    paddd           m5, m3, [t2+wq+400*4]
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m2
    mova [t2+wq+400*4], m3
    paddd           m4, m8
    paddd           m5, m8
    psrld           m4, 4              ; (a + 8) >> 4
    psrld           m5, 4
    pslld           m2, m4, 3
    pslld           m3, m5, 3
    paddd           m4, m2             ; ((a + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
    pavgw           m3, m6             ; (b + 2) >> 2
    movif32         t3, t3m
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m9, m10, m14
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*2 +4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m11            ; x * b * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m11
    psrld           m0, 12
    psrld           m1, 12
    mova [t3+wq*2+400*4+ 8], m0
    mova [t3+wq*2+400*4+24], m1
    add             wq, 16
    jl .hv1_loop
    mov            r10, t2
    mov             t2, t1
    mov             t1, r10
    ret
.v0: ; vertical boxsums + ab (even rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
.v0_loop:
    mova            m0, [t1+wq+400*0]
    mova            m4, [t1+wq+400*2]
    mova            m5, [t1+wq+400*4]
    paddw           m0, m0
    paddd           m4, m4
    paddd           m5, m5
    paddw           m1, m0, [t2+wq+400*0]
    paddd           m2, m4, [t2+wq+400*2]
    paddd           m3, m5, [t2+wq+400*4]
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m4
    mova [t2+wq+400*4], m5
    paddd           m2, m8
    paddd           m3, m8
    psrld           m2, 4              ; (a + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
    pavgw           m3, m6             ; (b + 2) >> 2
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m9, m10, m14
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*0+ 4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m11            ; x * b * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m11
    psrld           m0, 12
    psrld           m1, 12
    mova [t3+wq*2+400*0+ 8], m0
    mova [t3+wq*2+400*0+24], m1
    add             wq, 16
    jl .v0_loop
    ret
.v1: ; vertical boxsums + ab (odd rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
.v1_loop:
    mova            m0, [t1+wq+400*0]
    mova            m4, [t1+wq+400*2]
    mova            m5, [t1+wq+400*4]
    paddw           m1, m0, [t2+wq+400*0]
    paddd           m2, m4, [t2+wq+400*2]
    paddd           m3, m5, [t2+wq+400*4]
    mova [t2+wq+400*0], m0
    mova [t2+wq+400*2], m4
    mova [t2+wq+400*4], m5
    paddd           m2, m8
    paddd           m3, m8
    psrld           m2, 4              ; (a + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
    pavgw           m3, m6             ; (b + 2) >> 2
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m9, m10, m14
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*2+ 4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m11            ; x * b * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m11
    psrld           m0, 12
    psrld           m1, 12
    mova [t3+wq*2+400*4+ 8], m0
    mova [t3+wq*2+400*4+24], m1
    add             wq, 16
    jl .v1_loop
    mov            r10, t2
    mov             t2, t1
    mov             t1, r10
    ret
.prep_n: ; initial neighbor setup
    movif64         wq, r4
    movif32         wd, w1m
.prep_n_loop:
    movu            m0, [t4+wq*1+400*0+ 4]
    movu            m1, [t3+wq*2+400*0+ 8]
    movu            m2, [t3+wq*2+400*0+24]
    movu            m3, [t4+wq*1+400*0+ 2]
    movu            m4, [t3+wq*2+400*0+ 4]
    movu            m5, [t3+wq*2+400*0+20]
    paddw           m0, [t4+wq*1+400*0+ 0]
    paddd           m1, [t3+wq*2+400*0+ 0]
    paddd           m2, [t3+wq*2+400*0+16]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    psllw           m3, 2                ; a[-1] 444
    pslld           m4, 2                ; b[-1] 444
    pslld           m5, 2
    psubw           m3, m0               ; a[-1] 343
    psubd           m4, m1               ; b[-1] 343
    psubd           m5, m2
    mova [t4+wq*1+400*4], m3
    mova [t3+wq*2+400*8+ 0], m4
    mova [t3+wq*2+400*8+16], m5
    movu            m0, [t4+wq*1+400*2+ 4]
    movu            m1, [t3+wq*2+400*4+ 8]
    movu            m2, [t3+wq*2+400*4+24]
    movu            m3, [t4+wq*1+400*2+ 2]
    movu            m4, [t3+wq*2+400*4+ 4]
    movu            m5, [t3+wq*2+400*4+20]
    paddw           m0, [t4+wq*1+400*2+ 0]
    paddd           m1, [t3+wq*2+400*4+ 0]
    paddd           m2, [t3+wq*2+400*4+16]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    psllw           m3, 2                 ; a[ 0] 444
    pslld           m4, 2                 ; b[ 0] 444
    pslld           m5, 2
    mova [t4+wq*1+400* 6], m3
    mova [t3+wq*2+400*12+ 0], m4
    mova [t3+wq*2+400*12+16], m5
    psubw           m3, m0                ; a[ 0] 343
    psubd           m4, m1                ; b[ 0] 343
    psubd           m5, m2
    mova [t4+wq*1+400* 8], m3
    mova [t3+wq*2+400*16+ 0], m4
    mova [t3+wq*2+400*16+16], m5
    add             wq, 16
    jl .prep_n_loop
    ret
ALIGN function_align
.n0: ; neighbor + output (even rows)
    movif64         wq, r4
    movif32         wd, w1m
.n0_loop:
    movu            m3, [t4+wq*1+400*0+4]
    movu            m1, [t4+wq*1+400*0+2]
    paddw           m3, [t4+wq*1+400*0+0]
    paddw           m1, m3
    psllw           m1, 2                ; a[ 1] 444
    psubw           m2, m1, m3           ; a[ 1] 343
    paddw           m3, m2, [t4+wq*1+400*4]
    paddw           m3, [t4+wq*1+400*6]
    mova [t4+wq*1+400*4], m2
    mova [t4+wq*1+400*6], m1
    movu            m4, [t3+wq*2+400*0+8]
    movu            m1, [t3+wq*2+400*0+4]
    paddd           m4, [t3+wq*2+400*0+0]
    paddd           m1, m4
    pslld           m1, 2                ; b[ 1] 444
    psubd           m2, m1, m4           ; b[ 1] 343
    paddd           m4, m2, [t3+wq*2+400* 8+ 0]
    paddd           m4, [t3+wq*2+400*12+ 0]
    mova [t3+wq*2+400* 8+ 0], m2
    mova [t3+wq*2+400*12+ 0], m1
    movu            m5, [t3+wq*2+400*0+24]
    movu            m1, [t3+wq*2+400*0+20]
    paddd           m5, [t3+wq*2+400*0+16]
    paddd           m1, m5
    pslld           m1, 2
    psubd           m2, m1, m5
    paddd           m5, m2, [t3+wq*2+400* 8+16]
    paddd           m5, [t3+wq*2+400*12+16]
    mova [t3+wq*2+400* 8+16], m2
    mova [t3+wq*2+400*12+16], m1
    mova            m0, [dstq+wq]
    punpcklwd       m1, m0, m6
    punpcklwd       m2, m3, m6
    pmaddwd         m2, m1               ; a * src
    punpckhwd       m1, m0, m6
    punpckhwd       m3, m6
    pmaddwd         m3, m1
    psubd           m4, m2               ; b - a * src + (1 << 8)
    psubd           m5, m3
    psrad           m4, 9
    psrad           m5, 9
    packssdw        m4, m5
    pmulhrsw        m4, m7
    paddw           m0, m4
    pmaxsw          m0, m6
    pminsw          m0, m13
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n0_loop
    add           dstq, stridemp
    ret
ALIGN function_align
.n1: ; neighbor + output (odd rows)
    movif64         wq, r4
    movif32         wd, w1m
.n1_loop:
    movu            m3, [t4+wq*1+400*2+4]
    movu            m1, [t4+wq*1+400*2+2]
    paddw           m3, [t4+wq*1+400*2+0]
    paddw           m1, m3
    psllw           m1, 2                ; a[ 1] 444
    psubw           m2, m1, m3           ; a[ 1] 343
    paddw           m3, m2, [t4+wq*1+400*6]
    paddw           m3, [t4+wq*1+400*8]
    mova [t4+wq*1+400*6], m1
    mova [t4+wq*1+400*8], m2
    movu            m4, [t3+wq*2+400*4+8]
    movu            m1, [t3+wq*2+400*4+4]
    paddd           m4, [t3+wq*2+400*4+0]
    paddd           m1, m4
    pslld           m1, 2                ; b[ 1] 444
    psubd           m2, m1, m4           ; b[ 1] 343
    paddd           m4, m2, [t3+wq*2+400*12+ 0]
    paddd           m4, [t3+wq*2+400*16+ 0]
    mova [t3+wq*2+400*12+ 0], m1
    mova [t3+wq*2+400*16+ 0], m2
    movu            m5, [t3+wq*2+400*4+24]
    movu            m1, [t3+wq*2+400*4+20]
    paddd           m5, [t3+wq*2+400*4+16]
    paddd           m1, m5
    pslld           m1, 2
    psubd           m2, m1, m5
    paddd           m5, m2, [t3+wq*2+400*12+16]
    paddd           m5, [t3+wq*2+400*16+16]
    mova [t3+wq*2+400*12+16], m1
    mova [t3+wq*2+400*16+16], m2
    mova            m0, [dstq+wq]
    punpcklwd       m1, m0, m6
    punpcklwd       m2, m3, m6
    pmaddwd         m2, m1               ; a * src
    punpckhwd       m1, m0, m6
    punpckhwd       m3, m6
    pmaddwd         m3, m1
    psubd           m4, m2               ; b - a * src + (1 << 8)
    psubd           m5, m3
    psrad           m4, 9
    psrad           m5, 9
    packssdw        m4, m5
    pmulhrsw        m4, m7
    paddw           m0, m4
    pmaxsw          m0, m6
    pminsw          m0, m13
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n1_loop
    add           dstq, stridemp
    movif32       dstm, dstq
    ret

%if ARCH_X86_32
 %if STACK_ALIGNMENT < 16
  %assign extra_stack 10*16
 %else
  %assign extra_stack 8*16
 %endif
cglobal sgr_filter_mix_16bpc, 1, 7, 8, -400*66-48-extra_stack, \
                              dst, stride, left, lpf, w
 %if STACK_ALIGNMENT < 16
  %define dstm         dword [esp+calloff+16*8+4*0]
  %define stridemp     dword [esp+calloff+16*8+4*1]
  %define leftm        dword [esp+calloff+16*8+4*2]
  %define lpfm         dword [esp+calloff+16*8+4*3]
  %define w0m          dword [esp+calloff+16*8+4*4]
  %define hd           dword [esp+calloff+16*8+4*5]
  %define edgeb         byte [esp+calloff+16*8+4*6]
  %define edged        dword [esp+calloff+16*8+4*6]
  %define leftmp leftm
 %else
  %define w0m wm
  %define hd dword r5m
  %define edgeb  byte r7m
  %define edged dword r7m
 %endif
 %define hvsrcm dword [esp+calloff+4*0]
 %define w1m    dword [esp+calloff+4*1]
 %define t3m    dword [esp+calloff+4*2]
 %define t4m    dword [esp+calloff+4*3]
 %xdefine m8 m6
 %define  m9 [base+pd_8]
 %define m10 [base+pd_34816]
 %define m11 [base+pw_455_24]
 %define m12 [base+pw_164_24]
 %define m13 [esp+calloff+16*4]
 %define m14 [esp+calloff+16*5]
 %define m15 [esp+calloff+16*6]
 %define  m6 [esp+calloff+16*7]
 %define base r6-pw_455_24
 %assign calloff 0
 %if STACK_ALIGNMENT < 16
    mov        strideq, [rstk+stack_offset+ 8]
    mov          leftq, [rstk+stack_offset+12]
    mov           lpfq, [rstk+stack_offset+16]
    mov             wd, [rstk+stack_offset+20]
    mov           dstm, dstq
    mov       stridemp, strideq
    mov          leftm, leftq
    mov             r1, [rstk+stack_offset+24]
    mov             r2, [rstk+stack_offset+32]
    mov           lpfm, lpfq
    mov             hd, r1
    mov          edged, r2
 %endif
%else
cglobal sgr_filter_mix_16bpc, 4, 13, 16, -400*66-40, dst, stride, left, lpf, \
                                                     w, h, edge, params
%endif
%if ARCH_X86_64 || STACK_ALIGNMENT >= 16
    movifnidn       wd, wm
%endif
%if ARCH_X86_64
    mov        paramsq, r6mp
    movifnidn       hd, hm
    add             wd, wd
    mov          edged, r7m
    mova           m14, [paramsq]
    add           lpfq, wq
    mova            m9, [pd_8]
    lea             t1, [rsp+wq+44]
    mova           m10, [pd_34816]
    add           dstq, wq
    mova           m11, [pw_455_24]
    lea             t3, [rsp+wq*2+400*24+40]
    mova           m12, [pw_164_24]
    lea             t4, [rsp+wq+400*52+40]
    neg             wq
    pshufd         m15, m14, q2222 ; w0 w1
    punpcklwd      m14, m14
    pshufd         m13, m14, q0000 ; s0
    pshufd         m14, m14, q2222 ; s1
    pxor            m6, m6
    psllw          m15, 2
 DEFINE_ARGS dst, stride, left, lpf, _, h, edge, _, _, _, w
 %define lpfm [rsp]
%else
    mov             r1, [rstk+stack_offset+28] ; params
    LEA             r6, pw_455_24
    add             wd, wd
    mova            m2, [r1]
    add           lpfm, wq
    lea             t1, [rsp+extra_stack+wq+52]
    add           dstq, wq
    lea             t3, [rsp+extra_stack+wq*2+400*24+48]
    mov           dstm, dstq
    lea             t4, [rsp+extra_stack+wq+400*52+48]
    mov            t3m, t3
    mov            t4m, t4
    neg             wq
    pshuflw         m0, m2, q0000
    pshuflw         m1, m2, q2222
    pshufhw         m2, m2, q1010
    punpcklqdq      m0, m0 ; s0
    punpcklqdq      m1, m1 ; s1
    punpckhqdq      m2, m2 ; w0 w1
    mov            w1m, wd
    pxor            m3, m3
    psllw           m2, 2
    mova           m13, m0
    mova           m14, m1
    sub             wd, 4
    mova           m15, m2
    mova            m6, m3
    mov           lpfq, lpfm
    mov            w0m, wd
 %define strideq r5
%endif
    test         edgeb, 4 ; LR_HAVE_TOP
    jz .no_top
    call .h_top
    add           lpfq, stridemp
    mov             t2, t1
%if ARCH_X86_64
    call mangle(private_prefix %+ _sgr_filter_5x5_16bpc_ssse3).top_fixup
%else
    mov             wq, w0m
    call mangle(private_prefix %+ _sgr_filter_5x5_16bpc_ssse3).top_fixup_loop
%endif
    add             t1, 400*12
    call .h_top
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    add            r10, strideq
    mov           lpfm, r10 ; below
    movif32         t4, t4m
    call .hv0
.main:
    dec             hd
    jz .height1
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1
    call .prep_n
    sub             hd, 2
    jl .extend_bottom
.main_loop:
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv0
%if ARCH_X86_64
    test            hd, hd
%else
    mov             r4, hd
    test            r4, r4
%endif
    jz .odd_height
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1
    call .n0
    call .n1
    sub             hd, 2
    jge .main_loop
    test         edgeb, 8 ; LR_HAVE_BOTTOM
    jz .extend_bottom
    mov           lpfq, lpfm
    call .hv0_bottom
    movif32       lpfq, hvsrcm
    add           lpfq, stridemp
    call .hv1_bottom
.end:
    call .n0
    call .n1
.end2:
    RET
.height1:
    call .v1
    call .prep_n
    jmp .odd_height_end
.odd_height:
    call .v1
    call .n0
    call .n1
.odd_height_end:
    call .v0
    call .v1
    call .n0
    jmp .end2
.extend_bottom:
    call .v0
    call .v1
    jmp .end
.no_top:
    movif32    strideq, stridemp
    lea            r10, [lpfq+strideq*4]
    mov           lpfq, dstq
    lea            r10, [r10+strideq*2]
    mov           lpfm, r10
    call .h
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wq, w0m
    mov         hvsrcm, lpfq
%endif
    lea             t2, [t1+400*12]
.top_fixup_loop:
    mova            m0, [t1+wq+400* 0]
    mova            m1, [t1+wq+400* 2]
    mova            m2, [t1+wq+400* 4]
    paddw           m0, m0
    mova            m3, [t1+wq+400* 6]
    paddd           m1, m1
    mova            m4, [t1+wq+400* 8]
    paddd           m2, m2
    mova            m5, [t1+wq+400*10]
    mova [t2+wq+400* 0], m0
    mova [t2+wq+400* 2], m1
    mova [t2+wq+400* 4], m2
    mova [t2+wq+400* 6], m3
    mova [t2+wq+400* 8], m4
    mova [t2+wq+400*10], m5
    add             wq, 16
    jl .top_fixup_loop
    movif32         t3, t3m
    movif32         t4, t4m
    call .v0
    jmp .main
.h: ; horizontal boxsum
%assign stack_offset stack_offset+4
%assign calloff 4
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
 %define leftq r4
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 10
    jmp .h_main
.h_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, [base+sgr_lshuf5]
    jmp .h_main
.h_top:
%if ARCH_X86_64
    lea             wq, [r4-4]
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .h_extend_left
    movif32         wq, w0m
.h_loop:
    movu            m4, [lpfq+wq- 2]
.h_main:
    movu            m5, [lpfq+wq+14]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .h_have_right
    cmp             wd, -20
    jl .h_have_right
%if ARCH_X86_32
    pxor            m8, m8
%endif
    call mangle(private_prefix %+ _sgr_filter_5x5_16bpc_ssse3).extend_right
.h_have_right:
    palignr         m3, m5, m4, 2
    palignr         m0, m5, m4, 4
    paddw           m1, m3, m0
    punpcklwd       m2, m3, m0
    pmaddwd         m2, m2
    punpckhwd       m3, m0
    pmaddwd         m3, m3
    palignr         m0, m5, m4, 6
    paddw           m1, m0             ; sum3
    punpcklwd       m7, m0, m6
    pmaddwd         m7, m7
    punpckhwd       m0, m6
    pmaddwd         m0, m0
    paddd           m2, m7             ; sumsq3
    palignr         m5, m4, 8
    punpcklwd       m7, m5, m4
    paddd           m3, m0
    paddw           m0, m4, m5
    pmaddwd         m7, m7
    punpckhwd       m5, m4
    pmaddwd         m5, m5
    mova [t1+wq+400* 6], m1
    mova [t1+wq+400* 8], m2
    mova [t1+wq+400*10], m3
    paddw           m0, m1             ; sum5
    paddd           m7, m2             ; sumsq5
    paddd           m5, m3
    mova [t1+wq+400* 0], m0
    mova [t1+wq+400* 2], m7
    mova [t1+wq+400* 4], m5
    add             wq, 16
    jl .h_loop
    ret
ALIGN function_align
.hv0: ; horizontal boxsum + vertical boxsum + ab3 (even rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv0_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 10
    jmp .hv0_main
.hv0_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, [base+sgr_lshuf5]
    jmp .hv0_main
.hv0_bottom:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv0_extend_left
    movif32         wq, w0m
%if ARCH_X86_32
    jmp .hv0_loop_start
%endif
.hv0_loop:
    movif32       lpfq, hvsrcm
.hv0_loop_start:
    movu            m4, [lpfq+wq- 2]
.hv0_main:
    movu            m5, [lpfq+wq+14]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv0_have_right
    cmp             wd, -20
    jl .hv0_have_right
%if ARCH_X86_32
    pxor            m8, m8
%endif
    call mangle(private_prefix %+ _sgr_filter_5x5_16bpc_ssse3).extend_right
.hv0_have_right:
    palignr         m3, m5, m4, 2
    palignr         m0, m5, m4, 4
    movif32         t3, t3m
    paddw           m1, m3, m0
    punpcklwd       m2, m3, m0
    pmaddwd         m2, m2
    punpckhwd       m3, m0
    pmaddwd         m3, m3
    palignr         m0, m5, m4, 6
    paddw           m1, m0             ; h sum3
    punpcklwd       m7, m0, m6
    pmaddwd         m7, m7
    punpckhwd       m0, m6
    pmaddwd         m0, m0
    paddd           m2, m7             ; h sumsq3
    palignr         m5, m4, 8
    punpcklwd       m7, m5, m4
    paddd           m3, m0
    paddw           m0, m4, m5
    pmaddwd         m7, m7
    punpckhwd       m5, m4
    pmaddwd         m5, m5
    paddw           m0, m1             ; h sum5
    paddd           m7, m2             ; h sumsq5
    paddd           m5, m3
    mova [t3+wq*2+400*8+ 8], m0
    mova [t3+wq*2+400*0+ 8], m7
    mova [t3+wq*2+400*0+24], m5
    paddw           m0, [t1+wq+400* 0]
    paddd           m7, [t1+wq+400* 2]
    paddd           m5, [t1+wq+400* 4]
    mova [t1+wq+400* 0], m0
    mova [t1+wq+400* 2], m7
    mova [t1+wq+400* 4], m5
    paddw           m0, m1, [t1+wq+400* 6]
    paddd           m4, m2, [t1+wq+400* 8]
    paddd           m5, m3, [t1+wq+400*10]
    mova [t1+wq+400* 6], m1
    mova [t1+wq+400* 8], m2
    mova [t1+wq+400*10], m3
    paddw           m1, m0, [t2+wq+400* 6]
    paddd           m2, m4, [t2+wq+400* 8]
    paddd           m3, m5, [t2+wq+400*10]
    mova [t2+wq+400* 6], m0
    mova [t2+wq+400* 8], m4
    mova [t2+wq+400*10], m5
    paddd           m2, m9
    paddd           m3, m9
    movaps          m8, [base+pf_256]
    psrld           m2, 4              ; (a3 + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a3 + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
%if ARCH_X86_32
    pxor            m7, m7
    pavgw           m3, m7
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m7, m14, m11, m8
%else
    pavgw           m3, m6             ; (b3 + 2) >> 2
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m14, m11, m8
%endif
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*2+ 4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m10            ; x3 * b3 * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m10
    psrld           m0, 12
    psrld           m1, 12
    mova [t3+wq*2+400*4+ 8], m0
    mova [t3+wq*2+400*4+24], m1
    add             wq, 16
    jl .hv0_loop
    ret
ALIGN function_align
.hv1: ; horizontal boxsums + vertical boxsums + ab (odd rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv1_extend_left
    movif32      leftq, leftm
    movddup         m5, [leftq]
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    add         leftmp, 8
    palignr         m4, m5, 10
    jmp .hv1_main
.hv1_extend_left:
    movif32         wq, w0m
    mova            m4, [lpfq+wq+4]
    pshufb          m4, [base+sgr_lshuf5]
    jmp .hv1_main
.hv1_bottom:
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov         hvsrcm, lpfq
%endif
    test         edgeb, 1 ; LR_HAVE_LEFT
    jz .hv1_extend_left
    movif32         wq, w0m
%if ARCH_X86_32
    jmp .hv1_loop_start
%endif
.hv1_loop:
    movif32       lpfq, hvsrcm
.hv1_loop_start:
    movu            m4, [lpfq+wq- 2]
.hv1_main:
    movu            m5, [lpfq+wq+14]
    test         edgeb, 2 ; LR_HAVE_RIGHT
    jnz .hv1_have_right
    cmp             wd, -20
    jl .hv1_have_right
%if ARCH_X86_32
    pxor            m8, m8
%endif
    call mangle(private_prefix %+ _sgr_filter_5x5_16bpc_ssse3).extend_right
.hv1_have_right:
    palignr         m7, m5, m4, 2
    palignr         m3, m5, m4, 4
    paddw           m2, m7, m3
    punpcklwd       m0, m7, m3
    pmaddwd         m0, m0
    punpckhwd       m7, m3
    pmaddwd         m7, m7
    palignr         m3, m5, m4, 6
    paddw           m2, m3             ; h sum3
    punpcklwd       m1, m3, m6
    pmaddwd         m1, m1
    punpckhwd       m3, m6
    pmaddwd         m3, m3
    paddd           m0, m1             ; h sumsq3
    palignr         m5, m4, 8
    punpckhwd       m1, m4, m5
    paddw           m8, m4, m5
    pmaddwd         m1, m1
    punpcklwd       m4, m5
    pmaddwd         m4, m4
    paddd           m7, m3
    paddw           m5, m2, [t2+wq+400* 6]
    mova [t2+wq+400* 6], m2
    paddw           m8, m2             ; h sum5
    paddd           m2, m0, [t2+wq+400* 8]
    paddd           m3, m7, [t2+wq+400*10]
    mova [t2+wq+400* 8], m0
    mova [t2+wq+400*10], m7
    paddd           m4, m0             ; h sumsq5
    paddd           m1, m7
    paddd           m2, m9
    paddd           m3, m9
    psrld           m2, 4              ; (a3 + 8) >> 4
    psrld           m3, 4
    pslld           m0, m2, 3
    pslld           m7, m3, 3
    paddd           m2, m0             ; ((a3 + 8) >> 4) * 9
    paddd           m3, m7
    psrlw           m7, m5, 1
    pavgw           m7, m6             ; (b3 + 2) >> 2
%if ARCH_X86_32
    mova      [esp+20], m8
    mov             t3, t3m
    SGR_CALC_X      m0, m5, m7, m8, m2, m3, m6, m14, m11, [base+pf_256]
%else
    SGR_CALC_X      m0, m5, m7, m12, m2, m3, m6, m14, m11, [base+pf_256]
%endif
    punpcklwd       m2, m7, m7
    mova [t4+wq*1+400*4+4], m7
    punpckhwd       m7, m7
%if ARCH_X86_32
    MUL_32X16X2     m0, m5, m2, m7, m3, m8
    mova            m8, [esp+20]
%else
    MUL_32X16X2     m0, m5, m2, m7, m3, m12
    mova           m12, [pw_164_24]
%endif
    paddd           m0, m10            ; x3 * b3 * 455 + (1 << 11) + (1 << 15)
    paddd           m5, m10
    psrld           m0, 12
    psrld           m5, 12
    mova [t3+wq*2+400*8+ 8], m0
    mova [t3+wq*2+400*8+24], m5
    paddw           m5, m8, [t2+wq+400*0]
    paddd           m2, m4, [t2+wq+400*2]
    paddd           m3, m1, [t2+wq+400*4]
    paddw           m5, [t1+wq+400*0]
    paddd           m2, [t1+wq+400*2]
    paddd           m3, [t1+wq+400*4]
    mova [t2+wq+400*0], m8
    paddd           m2, m9
    paddd           m3, m9
    psrld           m2, 4              ; (a5 + 8) >> 4
    psrld           m3, 4
    mova [t2+wq+400*2], m4
    pslld           m8, m2, 4
    mova [t2+wq+400*4], m1
    pslld           m4, m3, 4
    paddd           m8, m2
    pslld           m2, 3
    paddd           m4, m3
    pslld           m3, 3
    paddd           m2, m8             ; ((a5 + 8) >> 4) * 25
    paddd           m3, m4
    psrlw           m1, m5, 1
%if ARCH_X86_32
    pxor            m7, m7
    pavgw           m1, m7
    SGR_CALC_X      m0, m5, m1, m4, m2, m3, m7, m13, m12, [base+pf_256]
%else
    movaps          m8, [base+pf_256]
    pavgw           m1, m6             ; (b5 + 2) >> 2
    SGR_CALC_X      m0, m5, m1, m4, m2, m3, m6, m13, m12, m8
%endif
    punpcklwd       m2, m1, m1
    mova [t4+wq*1+400*0+ 4], m1
    punpckhwd       m1, m1
    MUL_32X16X2     m0, m5, m2, m1, m3, m4
    paddd           m0, m10            ; x5 * b5 * 164 + (1 << 11) + (1 << 15)
    paddd           m5, m10
    psrld           m0, 12
    psrld           m5, 12
    mova [t3+wq*2+400*0+ 8], m0
    mova [t3+wq*2+400*0+24], m5
    add             wq, 16
    jl .hv1_loop
    mov            r10, t2
    mov             t2, t1
    mov             t1, r10
    ret
.v0: ; vertical boxsums + ab3 (even rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
    movaps          m8, [base+pf_256]
.v0_loop:
    mova            m0, [t1+wq+400* 6]
    mova            m4, [t1+wq+400* 8]
    mova            m5, [t1+wq+400*10]
    paddw           m0, m0
    paddd           m4, m4
    paddd           m5, m5
    paddw           m1, m0, [t2+wq+400* 6]
    paddd           m2, m4, [t2+wq+400* 8]
    paddd           m3, m5, [t2+wq+400*10]
    mova [t2+wq+400* 6], m0
    mova [t2+wq+400* 8], m4
    mova [t2+wq+400*10], m5
    paddd           m2, m9
    paddd           m3, m9
    psrld           m2, 4              ; (a3 + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a3 + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
%if ARCH_X86_32
    pxor            m7, m7
    pavgw           m3, m7
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m7, m14, m11, m8
%else
    pavgw           m3, m6             ; (b3 + 2) >> 2
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m14, m11, m8
%endif
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*2+4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m10            ; x3 * b3 * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m10
    psrld           m0, 12
    psrld           m1, 12
    mova            m3, [t1+wq+400*0]
    mova            m4, [t1+wq+400*2]
    mova            m5, [t1+wq+400*4]
    mova [t3+wq*2+400*8+ 8], m3
    mova [t3+wq*2+400*0+ 8], m4
    mova [t3+wq*2+400*0+24], m5
    paddw           m3, m3 ; cc5
    paddd           m4, m4
    paddd           m5, m5
    mova [t1+wq+400*0], m3
    mova [t1+wq+400*2], m4
    mova [t1+wq+400*4], m5
    mova [t3+wq*2+400*4+ 8], m0
    mova [t3+wq*2+400*4+24], m1
    add             wq, 16
    jl .v0_loop
    ret
.v1: ; vertical boxsums + ab (odd rows)
%if ARCH_X86_64
    lea             wq, [r4-4]
%else
    mov             wd, w0m
%endif
    movaps          m8, [base+pf_256]
.v1_loop:
    mova            m4, [t1+wq+400* 6]
    mova            m5, [t1+wq+400* 8]
    mova            m7, [t1+wq+400*10]
    paddw           m1, m4, [t2+wq+400* 6]
    paddd           m2, m5, [t2+wq+400* 8]
    paddd           m3, m7, [t2+wq+400*10]
    mova [t2+wq+400* 6], m4
    mova [t2+wq+400* 8], m5
    mova [t2+wq+400*10], m7
    paddd           m2, m9
    paddd           m3, m9
    psrld           m2, 4              ; (a3 + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 3
    pslld           m5, m3, 3
    paddd           m4, m2             ; ((a3 + 8) >> 4) * 9
    paddd           m5, m3
    psrlw           m3, m1, 1
%if ARCH_X86_32
    pxor            m7, m7
    pavgw           m3, m7
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m7, m14, m11, m8
%else
    pavgw           m3, m6             ; (b3 + 2) >> 2
    SGR_CALC_X      m0, m1, m3, m2, m4, m5, m6, m14, m11, m8
%endif
    punpcklwd       m2, m3, m3
    mova [t4+wq*1+400*4+4], m3
    punpckhwd       m3, m3
    MUL_32X16X2     m0, m1, m2, m3, m4, m5
    paddd           m0, m10            ; x3 * b3 * 455 + (1 << 11) + (1 << 15)
    paddd           m1, m10
    psrld           m0, 12
    psrld           m1, 12
    mova            m4, [t3+wq*2+400*8+ 8]
    mova            m5, [t3+wq*2+400*0+ 8]
    mova            m7, [t3+wq*2+400*0+24]
    mova [t3+wq*2+400*8+ 8], m0
    mova [t3+wq*2+400*8+24], m1
    paddw           m1, m4, [t2+wq+400*0]
    paddd           m2, m5, [t2+wq+400*2]
    paddd           m3, m7, [t2+wq+400*4]
    paddw           m1, [t1+wq+400*0]
    paddd           m2, [t1+wq+400*2]
    paddd           m3, [t1+wq+400*4]
    mova [t2+wq+400*0], m4
    mova [t2+wq+400*2], m5
    mova [t2+wq+400*4], m7
    paddd           m2, m9
    paddd           m3, m9
    psrld           m2, 4              ; (a5 + 8) >> 4
    psrld           m3, 4
    pslld           m4, m2, 4
    pslld           m5, m3, 4
    paddd           m4, m2
    pslld           m2, 3
    paddd           m5, m3
    pslld           m3, 3
    paddd           m2, m4
    paddd           m3, m5
    psrlw           m5, m1, 1
%if ARCH_X86_32
    pxor            m7, m7
    pavgw           m5, m7
    SGR_CALC_X      m0, m1, m5, m4, m2, m3, m7, m13, m12, m8
%else
    pavgw           m5, m6             ; (b5 + 2) >> 2
    SGR_CALC_X      m0, m1, m5, m4, m2, m3, m6, m13, m12, m8
%endif
    punpcklwd       m4, m5, m5
    mova [t4+wq*1+400*0+ 4], m5
    punpckhwd       m5, m5
    MUL_32X16X2     m0, m1, m4, m5, m2, m3
    paddd           m0, m10            ; x5 * b5 * 164 + (1 << 11) + (1 << 15)
    paddd           m1, m10
    psrld           m0, 12
    psrld           m1, 12
    mova [t3+wq*2+400*0+ 8], m0
    mova [t3+wq*2+400*0+24], m1
    add             wq, 16
    jl .v1_loop
    mov            r10, t2
    mov             t2, t1
    mov             t1, r10
    ret
.prep_n: ; initial neighbor setup
    movif64         wq, r4
    movif32         wd, w1m
.prep_n_loop:
    movu            m0, [t4+wq*1+400*0+ 2]
    movu            m1, [t3+wq*2+400*0+ 4]
    movu            m2, [t3+wq*2+400*0+20]
    movu            m3, [t4+wq*1+400*0+ 4]
    movu            m4, [t3+wq*2+400*0+ 8]
    paddw           m3, [t4+wq*1+400*0+ 0]
    paddd           m4, [t3+wq*2+400*0+ 0]
    paddd           m5, m2, [t3+wq*2+400*0+16]
    movu            m7, [t3+wq*2+400*0+24]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m7
    paddw           m0, m3
    paddd           m1, m4
    paddd           m2, m5
    psllw           m3, 2
    pslld           m4, 2
    pslld           m5, 2
    paddw           m0, m3               ; a5 565
    paddd           m1, m4               ; b5 565
    paddd           m2, m5
    mova [t4+wq*1+400* 6+ 0], m0
    mova [t3+wq*2+400*12+ 0], m1
    mova [t3+wq*2+400*12+16], m2
    movu            m0, [t4+wq*1+400*2+ 4]
    movu            m1, [t3+wq*2+400*4+ 8]
    movu            m2, [t3+wq*2+400*4+24]
    movu            m3, [t4+wq*1+400*2+ 2]
    movu            m4, [t3+wq*2+400*4+ 4]
    movu            m5, [t3+wq*2+400*4+20]
    paddw           m0, [t4+wq*1+400*2+ 0]
    paddd           m1, [t3+wq*2+400*4+ 0]
    paddd           m2, [t3+wq*2+400*4+16]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    psllw           m3, 2                ; a3[-1] 444
    pslld           m4, 2                ; b3[-1] 444
    pslld           m5, 2
    psubw           m3, m0               ; a3[-1] 343
    psubd           m4, m1               ; b3[-1] 343
    psubd           m5, m2
    mova [t4+wq*1+400* 8+ 0], m3
    mova [t3+wq*2+400*16+ 0], m4
    mova [t3+wq*2+400*16+16], m5
    movu            m0, [t4+wq*1+400*4+ 4]
    movu            m1, [t3+wq*2+400*8+ 8]
    movu            m2, [t3+wq*2+400*8+24]
    movu            m3, [t4+wq*1+400*4+ 2]
    movu            m4, [t3+wq*2+400*8+ 4]
    movu            m5, [t3+wq*2+400*8+20]
    paddw           m0, [t4+wq*1+400*4+ 0]
    paddd           m1, [t3+wq*2+400*8+ 0]
    paddd           m2, [t3+wq*2+400*8+16]
    paddw           m3, m0
    paddd           m4, m1
    paddd           m5, m2
    psllw           m3, 2                 ; a3[ 0] 444
    pslld           m4, 2                 ; b3[ 0] 444
    pslld           m5, 2
    mova [t4+wq*1+400*10+ 0], m3
    mova [t3+wq*2+400*20+ 0], m4
    mova [t3+wq*2+400*20+16], m5
    psubw           m3, m0                ; a3[ 0] 343
    psubd           m4, m1                ; b3[ 0] 343
    psubd           m5, m2
    mova [t4+wq*1+400*12+ 0], m3
    mova [t3+wq*2+400*24+ 0], m4
    mova [t3+wq*2+400*24+16], m5
    add             wq, 16
    jl .prep_n_loop
    ret
ALIGN function_align
.n0: ; neighbor + output (even rows)
    movif64         wq, r4
    movif32         wd, w1m
.n0_loop:
    movu            m0, [t4+wq*1+ 4]
    movu            m2, [t4+wq*1+ 2]
    paddw           m0, [t4+wq*1+ 0]
    paddw           m0, m2
    paddw           m2, m0
    psllw           m0, 2
    paddw           m0, m2               ; a5
    movu            m4, [t3+wq*2+ 8]
    movu            m5, [t3+wq*2+24]
    movu            m1, [t3+wq*2+ 4]
    movu            m3, [t3+wq*2+20]
    paddd           m4, [t3+wq*2+ 0]
    paddd           m5, [t3+wq*2+16]
    paddd           m4, m1
    paddd           m5, m3
    paddd           m1, m4
    paddd           m3, m5
    pslld           m4, 2
    pslld           m5, 2
    paddd           m4, m1               ; b5
    paddd           m5, m3
    movu            m2, [t4+wq*1+400* 6]
    paddw           m2, m0
    mova [t4+wq*1+400* 6], m0
    paddd           m0, m4, [t3+wq*2+400*12+ 0]
    paddd           m1, m5, [t3+wq*2+400*12+16]
    mova [t3+wq*2+400*12+ 0], m4
    mova [t3+wq*2+400*12+16], m5
    mova [rsp+16+ARCH_X86_32*4], m1
    movu            m3, [t4+wq*1+400*2+4]
    movu            m5, [t4+wq*1+400*2+2]
    paddw           m3, [t4+wq*1+400*2+0]
    paddw           m5, m3
    psllw           m5, 2                ; a3[ 1] 444
    psubw           m4, m5, m3           ; a3[ 1] 343
    movu            m3, [t4+wq*1+400* 8]
    paddw           m3, [t4+wq*1+400*10]
    paddw           m3, m4
    mova [t4+wq*1+400* 8], m4
    mova [t4+wq*1+400*10], m5
    movu            m1, [t3+wq*2+400*4+ 8]
    movu            m5, [t3+wq*2+400*4+ 4]
    movu            m7, [t3+wq*2+400*4+24]
    movu            m8, [t3+wq*2+400*4+20]
    paddd           m1, [t3+wq*2+400*4+ 0]
    paddd           m7, [t3+wq*2+400*4+16]
    paddd           m5, m1
    paddd           m8, m7
    pslld           m5, 2                ; b3[ 1] 444
    pslld           m8, 2
    psubd           m4, m5, m1           ; b3[ 1] 343
%if ARCH_X86_32
    mova      [esp+52], m8
    psubd           m8, m7
%else
    psubd           m6, m8, m7
    SWAP            m8, m6
%endif
    paddd           m1, m4, [t3+wq*2+400*16+ 0]
    paddd           m7, m8, [t3+wq*2+400*16+16]
    paddd           m1, [t3+wq*2+400*20+ 0]
    paddd           m7, [t3+wq*2+400*20+16]
    mova [t3+wq*2+400*16+ 0], m4
    mova [t3+wq*2+400*16+16], m8
    mova [t3+wq*2+400*20+ 0], m5
%if ARCH_X86_32
    mova            m8, [esp+52]
%else
    SWAP            m8, m6
    pxor            m6, m6
%endif
    mova [t3+wq*2+400*20+16], m8
    mova [rsp+32+ARCH_X86_32*4], m7
    movu            m5, [dstq+wq]
    punpcklwd       m4, m5, m6
    punpcklwd       m7, m2, m6
    pmaddwd         m7, m4               ; a5 * src
    punpcklwd       m8, m3, m6
    pmaddwd         m8, m4               ; a3 * src
    punpckhwd       m5, m6
    punpckhwd       m2, m6
    pmaddwd         m2, m5
    punpckhwd       m3, m6
    pmaddwd         m3, m5
    pslld           m4, 13
    pslld           m5, 13
    psubd           m0, m7               ; b5 - a5 * src + (1 << 8)
    psubd           m1, m8               ; b3 - a3 * src + (1 << 8)
    mova            m7, [base+pd_0xffff]
    psrld           m0, 9
    pslld           m1, 7
    pand            m0, m7
    pandn           m8, m7, m1
    por             m0, m8
    mova            m1, [rsp+16+ARCH_X86_32*4]
    mova            m8, [rsp+32+ARCH_X86_32*4]
    psubd           m1, m2
    psubd           m8, m3
    mova            m2, [base+pd_4096]
    psrld           m1, 9
    pslld           m8, 7
    pand            m1, m7
    pandn           m7, m8
    por             m1, m7
    pmaddwd         m0, m15
    pmaddwd         m1, m15
%if ARCH_X86_32
    pxor            m7, m7
%else
    SWAP            m7, m6
%endif
    paddd           m4, m2
    paddd           m5, m2
    paddd           m0, m4
    paddd           m1, m5
    psrad           m0, 8
    psrad           m1, 8
    packssdw        m0, m1               ; clip
    pmaxsw          m0, m7
    psrlw           m0, 5
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n0_loop
    add           dstq, stridemp
    ret
%if ARCH_X86_64
    SWAP            m6, m7
%endif
ALIGN function_align
.n1: ; neighbor + output (odd rows)
    movif64         wq, r4
    movif32         wd, w1m
.n1_loop:
    movu            m3, [t4+wq*1+400*4+4]
    movu            m5, [t4+wq*1+400*4+2]
    paddw           m3, [t4+wq*1+400*4+0]
    paddw           m5, m3
    psllw           m5, 2                ; a3[ 1] 444
    psubw           m4, m5, m3           ; a3[ 1] 343
    paddw           m3, m4, [t4+wq*1+400*12]
    paddw           m3, [t4+wq*1+400*10]
    mova [t4+wq*1+400*10], m5
    mova [t4+wq*1+400*12], m4
    movu            m1, [t3+wq*2+400*8+ 8]
    movu            m5, [t3+wq*2+400*8+ 4]
    movu            m7, [t3+wq*2+400*8+24]
    movu            m8, [t3+wq*2+400*8+20]
    paddd           m1, [t3+wq*2+400*8+ 0]
    paddd           m7, [t3+wq*2+400*8+16]
    paddd           m5, m1
    paddd           m8, m7
    pslld           m5, 2                ; b3[ 1] 444
    pslld           m8, 2
    psubd           m4, m5, m1           ; b3[ 1] 343
    psubd           m0, m8, m7
    paddd           m1, m4, [t3+wq*2+400*24+ 0]
    paddd           m7, m0, [t3+wq*2+400*24+16]
    paddd           m1, [t3+wq*2+400*20+ 0]
    paddd           m7, [t3+wq*2+400*20+16]
    mova [t3+wq*2+400*20+ 0], m5
    mova [t3+wq*2+400*20+16], m8
    mova [t3+wq*2+400*24+ 0], m4
    mova [t3+wq*2+400*24+16], m0
    mova            m5, [dstq+wq]
    mova            m2, [t4+wq*1+400* 6]
    punpcklwd       m4, m5, m6
    punpcklwd       m8, m2, m6
    pmaddwd         m8, m4               ; a5 * src
    punpcklwd       m0, m3, m6
    pmaddwd         m0, m4               ; a3 * src
    punpckhwd       m5, m6
    punpckhwd       m2, m6
    pmaddwd         m2, m5
    punpckhwd       m3, m6
    pmaddwd         m3, m5
    psubd           m1, m0               ; b3 - a3 * src + (1 << 8)
    pslld           m4, 13
    pslld           m5, 13
    mova            m0, [t3+wq*2+400*12+ 0]
    psubd           m0, m8               ; b5 - a5 * src + (1 << 8)
    mova            m8, [t3+wq*2+400*12+16]
    psubd           m8, m2
    psubd           m7, m3
    mova            m2, [base+pd_0xffff]
    pslld           m1, 7
    psrld           m0, 8
    psrld           m8, 8
    pslld           m7, 7
    pand            m0, m2
    pandn           m3, m2, m1
    por             m0, m3
    pand            m8, m2
    pandn           m2, m7
    por             m2, m8
    mova            m1, [base+pd_4096]
    pmaddwd         m0, m15
    pmaddwd         m2, m15
%if ARCH_X86_64
    SWAP            m7, m6
%endif
    pxor            m7, m7
    paddd           m4, m1
    paddd           m5, m1
    paddd           m0, m4
    paddd           m2, m5
    psrad           m0, 8
    psrad           m2, 8
    packssdw        m0, m2              ; clip
    pmaxsw          m0, m7
    psrlw           m0, 5
    mova     [dstq+wq], m0
    add             wq, 16
    jl .n1_loop
    add           dstq, stridemp
    movif32       dstm, dstq
    ret
