shithub: opus

ref: 41e8906cd65023ef76492098a0d9ed78a7c1c554
dir: /celt/arm/celt_pitch_xcorr_arm.s/

View raw version
; Copyright (c) 2007-2008 CSIRO
; Copyright (c) 2007-2009 Xiph.Org Foundation
; Copyright (c) 2013      Parrot
; Written by Aurélien Zanelli
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
;
; - Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
;
; - 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.

  AREA  |.text|, CODE, READONLY

  GET    celt/arm/armopts.s

IF OPUS_ARM_MAY_HAVE_EDSP
  EXPORT celt_pitch_xcorr_edsp
ENDIF

IF OPUS_ARM_MAY_HAVE_NEON
  EXPORT celt_pitch_xcorr_neon
ENDIF

IF OPUS_ARM_MAY_HAVE_NEON

;; Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3
;xcorr_kernel_neon PROC
;  ; input:
;  ;   r3     = int         len
;  ;   r4     = opus_val16 *x
;  ;   r5     = opus_val16 *y
;  ;   q0     = opus_val32  sum[4]
;  ; output:
;  ;   q0     = opus_val32  sum[4]
;  ; preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15
;  ; internal usage:
;  ;   r12 = int j
;  ;   d3  = y_3|y_2|y_1|y_0
;  ;   q2  = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4
;  ;   q3  = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0
;  ;   q8  = scratch
;  ;
;  ; Load y[0...3]
;  ; This requires len>0 to always be valid (which we assert in the C code).
;  VLD1.16      {d5}, [r5]!
;  SUBS         r12, r3, #8
;  BLE xcorr_kernel_neon_process4
;; Process 8 samples at a time.
;; This loop loads one y value more than we actually need. Therefore we have to
;; stop as soon as there are 8 or fewer samples left (instead of 7), to avoid
;; reading past the end of the array.
;xcorr_kernel_neon_process8
;  ; This loop has 19 total instructions (10 cycles to issue, minimum), with
;  ; - 2 cycles of ARM insrtuctions,
;  ; - 10 cycles of load/store/byte permute instructions, and
;  ; - 9 cycles of data processing instructions.
;  ; On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the
;  ; latter two categories, meaning the whole loop should run in 10 cycles per
;  ; iteration, barring cache misses.
;  ;
;  ; Load x[0...7]
;  VLD1.16      {d6, d7}, [r4]!
;  ; Unlike VMOV, VAND is a data processsing instruction (and doesn't get
;  ; assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1.
;  VAND         d3, d5, d5
;  SUBS         r12, r12, #8
;  ; Load y[4...11]
;  VLD1.16      {d4, d5}, [r5]!
;  VMLAL.S16    q0, d3, d6[0]
;  VEXT.16      d16, d3, d4, #1
;  VMLAL.S16    q0, d4, d7[0]
;  VEXT.16      d17, d4, d5, #1
;  VMLAL.S16    q0, d16, d6[1]
;  VEXT.16      d16, d3, d4, #2
;  VMLAL.S16    q0, d17, d7[1]
;  VEXT.16      d17, d4, d5, #2
;  VMLAL.S16    q0, d16, d6[2]
;  VEXT.16      d16, d3, d4, #3
;  VMLAL.S16    q0, d17, d7[2]
;  VEXT.16      d17, d4, d5, #3
;  VMLAL.S16    q0, d16, d6[3]
;  VMLAL.S16    q0, d17, d7[3]
;  BGT xcorr_kernel_neon_process8
;; Process 4 samples here if we have > 4 left (still reading one extra y value).
;xcorr_kernel_neon_process4
;  ADDS         r12, r12, #4
;  BLE xcorr_kernel_neon_process2
;  ; Load x[0...3]
;  VLD1.16      d6, [r4]!
;  ; Use VAND since it's a data processing instruction again.
;  VAND         d4, d5, d5
;  SUB          r12, r12, #4
;  ; Load y[4...7]
;  VLD1.16      d5, [r5]!
;  VMLAL.S16    q0, d4, d6[0]
;  VEXT.16      d16, d4, d5, #1
;  VMLAL.S16    q0, d16, d6[1]
;  VEXT.16      d16, d4, d5, #2
;  VMLAL.S16    q0, d16, d6[2]
;  VEXT.16      d16, d4, d5, #3
;  VMLAL.S16    q0, d16, d6[3]
;; Process 2 samples here if we have > 2 left (still reading one extra y value).
;xcorr_kernel_neon_process2
;  ADDS         r12, r12, #2
;  BLE xcorr_kernel_neon_process1
;  ; Load x[0...1]
;  VLD2.16      {d6[],d7[]}, [r4]!
;  ; Use VAND since it's a data processing instruction again.
;  VAND         d4, d5, d5
;  SUB          r12, r12, #2
;  ; Load y[4...5]
;  VLD1.32      {d5[]}, [r5]!
;  VMLAL.S16    q0, d4, d6
;  VEXT.16      d16, d4, d5, #1
;  ; Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI
;  ; instead of VEXT, since it's a data-processing instruction.
;  VSRI.64      d5, d4, #32
;  VMLAL.S16    q0, d16, d7
;; Process 1 sample using the extra y value we loaded above.
;xcorr_kernel_neon_process1
;  ; Load next *x
;  VLD1.16      {d6[]}, [r4]!
;  ADDS         r12, r12, #1
;  ; y[0...3] are left in d5 from prior iteration(s) (if any)
;  VMLAL.S16    q0, d5, d6
;  MOVLE        pc, lr
;; Now process 1 last sample, not reading ahead.
;  ; Load last *y
;  VLD1.16      {d4[]}, [r5]!
;  VSRI.64      d4, d5, #16
;  ; Load last *x
;  VLD1.16      {d6[]}, [r4]!
;  VMLAL.S16    q0, d4, d6
;  MOV          pc, lr
;  ENDP

;; opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y,
;;  opus_val32 *xcorr, int len, int max_pitch)
;celt_pitch_xcorr_neon PROC
;  ; input:
;  ;   r0  = opus_val16 *_x
;  ;   r1  = opus_val16 *_y
;  ;   r2  = opus_val32 *xcorr
;  ;   r3  = int         len
;  ; output:
;  ;   r0  = int         maxcorr
;  ; internal usage:
;  ;   r4  = opus_val16 *x (for xcorr_kernel_neon())
;  ;   r5  = opus_val16 *y (for xcorr_kernel_neon())
;  ;   r6  = int         max_pitch
;  ;   r12 = int         j
;  ;   q15 = int         maxcorr[4] (q15 is not used by xcorr_kernel_neon())
;  STMFD        sp!, {r4-r6, lr}
;  LDR          r6, [sp, #16]
;  VMOV.S32     q15, #1
;  ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done
;  SUBS         r6, r6, #4
;  BLT celt_pitch_xcorr_neon_process4_done
;celt_pitch_xcorr_neon_process4
;  ; xcorr_kernel_neon parameters:
;  ; r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0}
;  MOV          r4, r0
;  MOV          r5, r1
;  VEOR         q0, q0, q0
;  ; xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3.
;  ; So we don't save/restore any other registers.
;  BL xcorr_kernel_neon
;  SUBS         r6, r6, #4
;  VST1.32      {q0}, [r2]!
;  ; _y += 4
;  ADD          r1, r1, #8
;  VMAX.S32     q15, q15, q0
;  ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done
;  BGE celt_pitch_xcorr_neon_process4
;; We have less than 4 sums left to compute.
;celt_pitch_xcorr_neon_process4_done
;  ADDS         r6, r6, #4
;  ; Reduce maxcorr to a single value
;  VMAX.S32     d30, d30, d31
;  VPMAX.S32    d30, d30, d30
;  ; if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done
;  BLE celt_pitch_xcorr_neon_done
;; Now compute each remaining sum one at a time.
;celt_pitch_xcorr_neon_process_remaining
;  MOV          r4, r0
;  MOV          r5, r1
;  VMOV.I32     q0, #0
;  SUBS         r12, r3, #8
;  BLT celt_pitch_xcorr_neon_process_remaining4
;; Sum terms 8 at a time.
;celt_pitch_xcorr_neon_process_remaining_loop8
;  ; Load x[0...7]
;  VLD1.16      {q1}, [r4]!
;  ; Load y[0...7]
;  VLD1.16      {q2}, [r5]!
;  SUBS         r12, r12, #8
;  VMLAL.S16    q0, d4, d2
;  VMLAL.S16    q0, d5, d3
;  BGE celt_pitch_xcorr_neon_process_remaining_loop8
;; Sum terms 4 at a time.
;celt_pitch_xcorr_neon_process_remaining4
;  ADDS         r12, r12, #4
;  BLT celt_pitch_xcorr_neon_process_remaining4_done
;  ; Load x[0...3]
;  VLD1.16      {d2}, [r4]!
;  ; Load y[0...3]
;  VLD1.16      {d3}, [r5]!
;  SUB          r12, r12, #4
;  VMLAL.S16    q0, d3, d2
;  ; Reduce the sum to a single value.
;  VADD.S32     d0, d0, d1
;  VPADDL.S32   d0, d0
;celt_pitch_xcorr_neon_process_remaining4_done
;  ADDS         r12, r12, #4
;  BLE celt_pitch_xcorr_neon_process_remaining_loop_done
;; Sum terms 1 at a time.
;celt_pitch_xcorr_neon_process_remaining_loop1
;  VLD1.16      {d2[]}, [r4]!
;  VLD1.16      {d3[]}, [r5]!
;  SUBS         r12, r12, #1
;  VMLAL.S16    q0, d2, d3
;  BGT celt_pitch_xcorr_neon_process_remaining_loop1
;celt_pitch_xcorr_neon_process_remaining_loop_done
;  VST1.32      {d0[0]}, [r2]!
;  VMAX.S32     d30, d30, d0
;  SUBS         r6, r6, #1
;  ; _y++
;  ADD          r1, r1, #2
;  ; if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining
;  BGT celt_pitch_xcorr_neon_process_remaining
;celt_pitch_xcorr_neon_done
;  VMOV.32      r0, d30[0]
;  LDMFD        sp!, {r4-r6, pc}
;  ENDP

xcorr_kernel_neon PROC
  ; input:
  ; r0 = opus_val16 *x
  ; r1 = opus_val16 *y
  ; r2 = int        len
  ; q0 = opus_val32 sum (sum[3] | sum[2] | sum[1] | sum[0])

  ; output:
  ; q0 = sum

  ; internal usage:
  ; r3 = j
  ; d2 = x_3|x_2|x_1|x_0  d3 = y_3|y_2|y_1|y_0
  ; d4 = y_7|y_6|y_5|y_4  d5 = y_4|y_3|y_2|y_1
  ; d6 = y_5|y_4|y_3|y_2  d7 = y_6|y_5|y_4|y_3
  ; We will build d5, d6 and d7 vector from d3 and d4


  VLD1.16   {d3}, [r1]!      ; Load y[3] downto y[0] to d3 lane (yy0)
  SUB       r3, r2, #1
  MOVS      r3, r3, lsr #2   ; j=(len-1)>>2
  BEQ       xcorr_kernel_neon_process4_done

  ; Process 4 x samples at a time
  ; For this, we will need 4 y vectors
xcorr_kernel_neon_process4
  SUBS      r3, r3, #1       ; j--
  VLD1.16   d4, [r1]!        ; Load y[7] downto y[4] to d4 lane
  VLD1.16   d2, [r0]!        ; Load x[3] downto x[0] to d2 lane
  VEXT.16   d5, d3, d4, #1   ; Build y[4] downto y[1] vector (yy1)
  VEXT.16   d6, d3, d4, #2   ; Build y[5] downto y[2] vector (yy2)
  VEXT.16   d7, d3, d4, #3   ; Build y[6] downto y[3] vector (yy3)

  VMLAL.S16 q0, d3, d2[0]    ; MAC16_16(sum, x[0], yy0)
  VMLAL.S16 q0, d5, d2[1]    ; MAC16_16(sum, x[1], yy1)
  VMLAL.S16 q0, d6, d2[2]    ; MAC16_16(sum, x[2], yy2)
  VMLAL.S16 q0, d7, d2[3]    ; MAC16_16(sum, x[3], yy3)

  VMOV.S16  d3, d4           ; Next y vector should be in d3 (yy0)

  BNE xcorr_kernel_neon_process4

xcorr_kernel_neon_process4_done
  ;Process len-1 to len
  VLD1.16   {d2[]}, [r0]!    ; Load *x and duplicate to d2 lane

  SUB       r3, r2, #1
  ANDS      r3, r3, #3       ; j=(len-1)&3
  VMLAL.S16 q0, d3, d2       ; MAC16_16(sum, *x, yy0)
  BEQ xcorr_kernel_neon_done

xcorr_kernel_neon_process_remaining
  SUBS      r3, r3, #1       ; j--
  VLD1.16   {d4[]}, [r1]!    ; Load y value and duplicate to d4 lane
  VLD1.16   {d2[]}, [r0]!    ; Load *x and duplicate to d2 lane
  VEXT.16   d3, d3, d4, #1   ; Build y vector from previous and d4
  VMLAL.S16 q0, d3, d2       ; MAC16_16(sum, *x, yy0)
  BNE xcorr_kernel_neon_process_remaining

xcorr_kernel_neon_done
  MOV       pc, lr
  ENDP

celt_pitch_xcorr_neon PROC
  ; input:
  ; r0 = opus_val16 *_x
  ; r1 = opus_val16 *_y
  ; r2 = opus_val32 *xcorr
  ; r3 = int        len

  ; output:
  ; r0 = maxcorr

  STMFD     sp!, {r4-r9, lr}

  LDR       r4, [sp, #28]        ; r4 = int max_pitch
  MOV       r5, r0               ; r5 = _x
  MOV       r6, r1               ; r6 = _y
  MOV       r7, r2               ; r7 = xcorr
  MOV       r2, r3               ; r2 = len

  VMOV.S32  d16, #1              ; d16 = {1, 1}  (not used by xcorr_kernel_neon)
  MOV       r8, #0               ; r8 = i = 0
  CMP       r4, #3               ; max_pitch-3 <= 0  ---> pitch_xcorr_neon_process4_done
  BLE       celt_pitch_xcorr_neon_process4_done

  SUB       r9, r4, #3           ; r9 = max_pitch-3

celt_pitch_xcorr_neon_process4
  MOV       r0, r5               ; r0 = _x
  ADD       r1, r6 ,r8, LSL #1   ; r1 = _y + i
  VMOV.I32  q0, #0               ; q0 = opus_val32 sum[4] = {0, 0, 0, 0}

                                 ; xcorr_kernel_neon don't touch r2 (len)
                                 ; So we don't store it
  BL xcorr_kernel_neon           ; xcorr_kernel_neon(_x, _y+i, sum, len)

  VST1.32   {q0}, [r7]!          ; Store sum to xcorr
  VPMAX.S32 d0, d0, d1           ; d0 = max(sum[3], sum[2]) | max(sum[1], sum[0])
  ADD       r8, r8, #4           ; i+=4
  VPMAX.S32 d0, d0, d0           ; d0 = max(sum[3], sum[2], sum[1], sum[0])
  CMP       r8, r9               ; i < max_pitch-3 ----> pitch_xcorr_neon_process4
  VMAX.S32  d16, d16, d0         ; d16 = maxcorr = max(maxcorr, sum)

  BLT       celt_pitch_xcorr_neon_process4

celt_pitch_xcorr_neon_process4_done
  CMP       r8, r4;
  BGE       celt_pitch_xcorr_neon_done

celt_pitch_xcorr_neon_process_remaining
  MOV       r0, r5               ; r0 = _x
  ADD       r1, r6, r8, LSL #1   ; r1 = _y + i
  VMOV.I32  q0, #0
  MOVS      r3, r2, LSR #2       ; r3 = j = len
  BEQ       inner_loop_neon_process4_done

inner_loop_neon_process4
  VLD1.16   {d2}, [r0]!          ; Load x
  VLD1.16   {d3}, [r1]!          ; Load y
  SUBS      r3, r3, #1
  VMLAL.S16 q0, d2, d3
  BNE       inner_loop_neon_process4

  VPADD.S32 d0, d0, d1          ; Reduce sum
  VPADD.S32 d0, d0, d0

inner_loop_neon_process4_done
  ANDS      r3, r2, #3
  BEQ       inner_loop_neon_done

inner_loop_neon_process_remaining
  VLD1.16   {d2[]}, [r0]!
  VLD1.16   {d3[]}, [r1]!
  SUBS      r3, r3, #1
  VMLAL.S16 q0, d2, d3
  BNE       inner_loop_neon_process_remaining

inner_loop_neon_done
  VST1.32   {d0[0]}, [r7]!
  VMAX.S32  d16, d16, d0

  ADD       r8, r8, #1
  CMP       r8, r4
  BCC       celt_pitch_xcorr_neon_process_remaining

celt_pitch_xcorr_neon_done
  VMOV      d0, d16
  VMOV.32   r0, d0[0]
  LDMFD     sp!, {r4-r9, pc}
  ENDP


ENDIF

IF OPUS_ARM_MAY_HAVE_EDSP

; This will get used on ARMv7 devices without NEON, so it has been optimized
; to take advantage of dual-issuing where possible.
xcorr_kernel_edsp PROC
  ; input:
  ;   r3      = int         len
  ;   r4      = opus_val16 *_x
  ;   r5      = opus_val16 *_y
  ;   r6...r9 = opus_val32  sum[4]
  ; output:
  ;   r6...r9 = opus_val32  sum[4]
  ; preserved: r0-r5
  ; internal usage
  ;   r2      = int         j
  ;   r12,r14 = opus_val16  x[4]
  ;   r10,r11 = opus_val16  y[4]
  STMFD        sp!, {r2,r4,r5,lr}
  SUBS         r2, r3, #4         ; j = len-4
  LDRD         r10, r11, [r5], #8 ; Load y[0...3]
  BLE xcorr_kernel_edsp_process4_done
  LDR          r12, [r4], #4      ; Load x[0...1]
  ; Stall
xcorr_kernel_edsp_process4
  ; The multiplies must issue from pipeline 0, and can't dual-issue with each
  ; other. Every other instruction here dual-issues with a multiply, and is
  ; thus "free". There should be no stalls in the body of the loop.
  SMLABB       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x_0,y_0)
  LDR          r14, [r4], #4      ; Load x[2...3]
  SMLABT       r7, r12, r10, r7   ; sum[1] = MAC16_16(sum[1],x_0,y_1)
  SUBS         r2, r2, #4         ; j-=4
  SMLABB       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x_0,y_2)
  SMLABT       r9, r12, r11, r9   ; sum[3] = MAC16_16(sum[3],x_0,y_3)
  SMLATT       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x_1,y_1)
  LDR          r10, [r5], #4      ; Load y[4...5]
  SMLATB       r7, r12, r11, r7   ; sum[1] = MAC16_16(sum[1],x_1,y_2)
  SMLATT       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x_1,y_3)
  SMLATB       r9, r12, r10, r9   ; sum[3] = MAC16_16(sum[3],x_1,y_4)
  LDRGT        r12, [r4], #4      ; Load x[0...1]
  SMLABB       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],x_2,y_2)
  SMLABT       r7, r14, r11, r7   ; sum[1] = MAC16_16(sum[1],x_2,y_3)
  SMLABB       r8, r14, r10, r8   ; sum[2] = MAC16_16(sum[2],x_2,y_4)
  SMLABT       r9, r14, r10, r9   ; sum[3] = MAC16_16(sum[3],x_2,y_5)
  SMLATT       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],x_3,y_3)
  LDR          r11, [r5], #4      ; Load y[6...7]
  SMLATB       r7, r14, r10, r7   ; sum[1] = MAC16_16(sum[1],x_3,y_4)
  SMLATT       r8, r14, r10, r8   ; sum[2] = MAC16_16(sum[2],x_3,y_5)
  SMLATB       r9, r14, r11, r9   ; sum[3] = MAC16_16(sum[3],x_3,y_6)
  BGT xcorr_kernel_edsp_process4
xcorr_kernel_edsp_process4_done
  ADDS         r2, r2, #4
  BLE xcorr_kernel_edsp_done
  LDRH         r12, [r4], #2      ; r12 = *x++
  SUBS         r2, r2, #1         ; j--
  ; Stall
  SMLABB       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x,y_0)
  LDRGTH       r14, [r4], #2      ; r14 = *x++
  SMLABT       r7, r12, r10, r7   ; sum[1] = MAC16_16(sum[1],x,y_1)
  SMLABB       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x,y_2)
  SMLABT       r9, r12, r11, r9   ; sum[3] = MAC16_16(sum[3],x,y_3)
  BLE xcorr_kernel_edsp_done
  SMLABT       r6, r14, r10, r6   ; sum[0] = MAC16_16(sum[0],x,y_1)
  SUBS         r2, r2, #1         ; j--
  SMLABB       r7, r14, r11, r7   ; sum[1] = MAC16_16(sum[1],x,y_2)
  LDRH         r10, [r5], #2      ; r10 = y_4 = *y++
  SMLABT       r8, r14, r11, r8   ; sum[2] = MAC16_16(sum[2],x,y_3)
  LDRGTH       r12, [r4], #2      ; r12 = *x++
  SMLABB       r9, r14, r10, r9   ; sum[3] = MAC16_16(sum[3],x,y_4)
  BLE xcorr_kernel_edsp_done
  SMLABB       r6, r12, r11, r6   ; sum[0] = MAC16_16(sum[0],tmp,y_2)
  CMP          r2, #1             ; j--
  SMLABT       r7, r12, r11, r7   ; sum[1] = MAC16_16(sum[1],tmp,y_3)
  LDRH         r2, [r5], #2       ; r2 = y_5 = *y++
  SMLABB       r8, r12, r10, r8   ; sum[2] = MAC16_16(sum[2],tmp,y_4)
  LDRGTH       r14, [r4]          ; r14 = *x
  SMLABB       r9, r12, r2, r9    ; sum[3] = MAC16_16(sum[3],tmp,y_5)
  BLE xcorr_kernel_edsp_done
  SMLABT       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],tmp,y_3)
  LDRH         r11, [r5]          ; r11 = y_6 = *y
  SMLABB       r7, r14, r10, r7   ; sum[1] = MAC16_16(sum[1],tmp,y_4)
  SMLABB       r8, r14, r2, r8    ; sum[2] = MAC16_16(sum[2],tmp,y_5)
  SMLABB       r9, r14, r11, r9   ; sum[3] = MAC16_16(sum[3],tmp,y_6)
xcorr_kernel_edsp_done
  LDMFD        sp!, {r2,r4,r5,pc}
  ENDP

celt_pitch_xcorr_edsp PROC
  ; input:
  ;   r0  = opus_val16 *_x
  ;   r1  = opus_val16 *_y
  ;   r2  = opus_val32 *xcorr
  ;   r3  = int         len
  ; output:
  ;   r0  = maxcorr
  ; internal usage
  ;   r4  = opus_val16 *x
  ;   r5  = opus_val16 *y
  ;   r6  = opus_val32  sum0
  ;   r7  = opus_val32  sum1
  ;   r8  = opus_val32  sum2
  ;   r9  = opus_val32  sum3
  ;   r1  = int         max_pitch
  ;   r12 = int         j
  STMFD        sp!, {r4-r11, lr}
  MOV          r5, r1
  LDR          r1, [sp, #36]
  MOV          r4, r0
  ; maxcorr = 1
  MOV          r0, #1
  ; if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process4_done
  SUBS         r1, r1, #4
  BLT celt_pitch_xcorr_edsp_process4_done
celt_pitch_xcorr_edsp_process4
  ; xcorr_kernel_edsp parameters:
  ; r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0}
  MOV          r6, #0
  MOV          r7, #0
  MOV          r8, #0
  MOV          r9, #0
  BL xcorr_kernel_edsp  ; xcorr_kernel_edsp(_x, _y+i, xcorr+i, len)
  ; maxcorr = max(maxcorr, sum0, sum1, sum2, sum3)
  CMP          r0, r6
  ; _y+=4
  ADD          r5, r5, #8
  MOVLT        r0, r6
  CMP          r0, r7
  STRD         r6, r7, [r2], #8
  MOVLT        r0, r7
  CMP          r0, r8
  STRD         r8, r9, [r2], #8
  MOVLT        r0, r8
  CMP          r0, r9
  MOVLT        r0, r9
  SUBS         r1, r1, #4
  BGE celt_pitch_xcorr_edsp_process4
celt_pitch_xcorr_edsp_process4_done
  ADDS         r1, r1, #4
  BLE celt_pitch_xcorr_edsp_done
; Now compute each remaining sum one at a time.
celt_pitch_xcorr_edsp_process_remaining
  SUBS         r12, r3, #4
  ; r14 = sum = 0
  MOV          r14, #0
  BLT celt_pitch_xcorr_edsp_process_remaining_loop_done
  LDRD         r6, r7, [r4], #8
  LDRD         r8, r9, [r5], #8
  ; Stall
celt_pitch_xcorr_edsp_process_remaining_loop4
  SMLABB       r14, r6, r8, r14     ; sum = MAC16_16(sum, x_0, y_0)
  SUBS         r12, r12, #4         ; j--
  SMLATT       r14, r6, r8, r14     ; sum = MAC16_16(sum, x_1, y_1)
  LDRGE        r6, [r4], #4
  SMLABB       r14, r7, r9, r14     ; sum = MAC16_16(sum, x_2, y_2)
  LDRGE        r8, [r5], #4
  SMLATT       r14, r7, r9, r14     ; sum = MAC16_16(sum, x_3, y_3)
  LDRGE        r7, [r4], #4
  LDRGE        r9, [r5], #4
  BGE celt_pitch_xcorr_edsp_process_remaining_loop4
celt_pitch_xcorr_edsp_process_remaining_loop_done
  ADDS         r12, r12, #2
  LDRGE        r6, [r4], #4
  LDRGE        r8, [r5], #4
  ; Stall
  SMLABBGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, x_0, y_0)
  SUBGE        r12, r12, #2
  SMLATTGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, x_1, y_1)
  ADDS         r12, r12, #1
  LDRGEH       r6, [r4], #2
  LDRGEH       r8, [r5], #2
  ; Restore _x
  SUB          r4, r4, r3, LSL #1
  ; Stall
  SMLABBGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, *x, *y)
  ; Restore and advance _y
  SUB          r5, r5, r3, LSL #1
  ; maxcorr = max(maxcorr, sum)
  ; Stall
  CMP          r0, r14
  ADD          r5, r5, #2
  MOVLT        r0, r14
  SUBS         r1, r1, #1
  ; xcorr[i] = sum
  STR          r14, [r2], #4
  BGT celt_pitch_xcorr_edsp_process_remaining
celt_pitch_xcorr_edsp_done
  LDMFD        sp!, {r4-r11, pc}
  ENDP

ENDIF

END