0f1bf5f6cb2472f4ff623f0f21e8cbbd96987f32
[opus.git] / celt / arm / celt_pitch_xcorr_arm.s
1 ; Copyright (c) 2007-2008 CSIRO
2 ; Copyright (c) 2007-2009 Xiph.Org Foundation
3 ; Copyright (c) 2013      Parrot
4 ; Written by AurĂ©lien Zanelli
5 ;
6 ; Redistribution and use in source and binary forms, with or without
7 ; modification, are permitted provided that the following conditions
8 ; are met:
9 ;
10 ; - Redistributions of source code must retain the above copyright
11 ; notice, this list of conditions and the following disclaimer.
12 ;
13 ; - Redistributions in binary form must reproduce the above copyright
14 ; notice, this list of conditions and the following disclaimer in the
15 ; documentation and/or other materials provided with the distribution.
16 ;
17 ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 ; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 ; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 ; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 ; OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 ; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 ; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 ; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 ; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 ; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29   AREA  |.text|, CODE, READONLY
30
31   GET    celt/arm/armopts.s
32
33 IF OPUS_ARM_MAY_HAVE_EDSP
34   EXPORT celt_pitch_xcorr_edsp
35 ENDIF
36
37 IF OPUS_ARM_MAY_HAVE_NEON
38   EXPORT celt_pitch_xcorr_neon
39 ENDIF
40
41 IF OPUS_ARM_MAY_HAVE_NEON
42
43 ;; Compute sum[k]=sum(x[j]*y[j+k],j=0...len-1), k=0...3
44 ;xcorr_kernel_neon PROC
45 ;  ; input:
46 ;  ;   r3     = int         len
47 ;  ;   r4     = opus_val16 *x
48 ;  ;   r5     = opus_val16 *y
49 ;  ;   q0     = opus_val32  sum[4]
50 ;  ; output:
51 ;  ;   q0     = opus_val32  sum[4]
52 ;  ; preserved: r0-r3, r6-r11, d2, q4-q7, q9-q15
53 ;  ; internal usage:
54 ;  ;   r12 = int j
55 ;  ;   d3  = y_3|y_2|y_1|y_0
56 ;  ;   q2  = y_B|y_A|y_9|y_8|y_7|y_6|y_5|y_4
57 ;  ;   q3  = x_7|x_6|x_5|x_4|x_3|x_2|x_1|x_0
58 ;  ;   q8  = scratch
59 ;  ;
60 ;  ; Load y[0...3]
61 ;  ; This requires len>0 to always be valid (which we assert in the C code).
62 ;  VLD1.16      {d5}, [r5]!
63 ;  SUBS         r12, r3, #8
64 ;  BLE xcorr_kernel_neon_process4
65 ;; Process 8 samples at a time.
66 ;; This loop loads one y value more than we actually need. Therefore we have to
67 ;; stop as soon as there are 8 or fewer samples left (instead of 7), to avoid
68 ;; reading past the end of the array.
69 ;xcorr_kernel_neon_process8
70 ;  ; This loop has 19 total instructions (10 cycles to issue, minimum), with
71 ;  ; - 2 cycles of ARM insrtuctions,
72 ;  ; - 10 cycles of load/store/byte permute instructions, and
73 ;  ; - 9 cycles of data processing instructions.
74 ;  ; On a Cortex A8, we dual-issue the maximum amount (9 cycles) between the
75 ;  ; latter two categories, meaning the whole loop should run in 10 cycles per
76 ;  ; iteration, barring cache misses.
77 ;  ;
78 ;  ; Load x[0...7]
79 ;  VLD1.16      {d6, d7}, [r4]!
80 ;  ; Unlike VMOV, VAND is a data processsing instruction (and doesn't get
81 ;  ; assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1.
82 ;  VAND         d3, d5, d5
83 ;  SUBS         r12, r12, #8
84 ;  ; Load y[4...11]
85 ;  VLD1.16      {d4, d5}, [r5]!
86 ;  VMLAL.S16    q0, d3, d6[0]
87 ;  VEXT.16      d16, d3, d4, #1
88 ;  VMLAL.S16    q0, d4, d7[0]
89 ;  VEXT.16      d17, d4, d5, #1
90 ;  VMLAL.S16    q0, d16, d6[1]
91 ;  VEXT.16      d16, d3, d4, #2
92 ;  VMLAL.S16    q0, d17, d7[1]
93 ;  VEXT.16      d17, d4, d5, #2
94 ;  VMLAL.S16    q0, d16, d6[2]
95 ;  VEXT.16      d16, d3, d4, #3
96 ;  VMLAL.S16    q0, d17, d7[2]
97 ;  VEXT.16      d17, d4, d5, #3
98 ;  VMLAL.S16    q0, d16, d6[3]
99 ;  VMLAL.S16    q0, d17, d7[3]
100 ;  BGT xcorr_kernel_neon_process8
101 ;; Process 4 samples here if we have > 4 left (still reading one extra y value).
102 ;xcorr_kernel_neon_process4
103 ;  ADDS         r12, r12, #4
104 ;  BLE xcorr_kernel_neon_process2
105 ;  ; Load x[0...3]
106 ;  VLD1.16      d6, [r4]!
107 ;  ; Use VAND since it's a data processing instruction again.
108 ;  VAND         d4, d5, d5
109 ;  SUB          r12, r12, #4
110 ;  ; Load y[4...7]
111 ;  VLD1.16      d5, [r5]!
112 ;  VMLAL.S16    q0, d4, d6[0]
113 ;  VEXT.16      d16, d4, d5, #1
114 ;  VMLAL.S16    q0, d16, d6[1]
115 ;  VEXT.16      d16, d4, d5, #2
116 ;  VMLAL.S16    q0, d16, d6[2]
117 ;  VEXT.16      d16, d4, d5, #3
118 ;  VMLAL.S16    q0, d16, d6[3]
119 ;; Process 2 samples here if we have > 2 left (still reading one extra y value).
120 ;xcorr_kernel_neon_process2
121 ;  ADDS         r12, r12, #2
122 ;  BLE xcorr_kernel_neon_process1
123 ;  ; Load x[0...1]
124 ;  VLD2.16      {d6[],d7[]}, [r4]!
125 ;  ; Use VAND since it's a data processing instruction again.
126 ;  VAND         d4, d5, d5
127 ;  SUB          r12, r12, #2
128 ;  ; Load y[4...5]
129 ;  VLD1.32      {d5[]}, [r5]!
130 ;  VMLAL.S16    q0, d4, d6
131 ;  VEXT.16      d16, d4, d5, #1
132 ;  ; Replace bottom copy of {y5,y4} in d5 with {y3,y2} from d4, using VSRI
133 ;  ; instead of VEXT, since it's a data-processing instruction.
134 ;  VSRI.64      d5, d4, #32
135 ;  VMLAL.S16    q0, d16, d7
136 ;; Process 1 sample using the extra y value we loaded above.
137 ;xcorr_kernel_neon_process1
138 ;  ; Load next *x
139 ;  VLD1.16      {d6[]}, [r4]!
140 ;  ADDS         r12, r12, #1
141 ;  ; y[0...3] are left in d5 from prior iteration(s) (if any)
142 ;  VMLAL.S16    q0, d5, d6
143 ;  MOVLE        pc, lr
144 ;; Now process 1 last sample, not reading ahead.
145 ;  ; Load last *y
146 ;  VLD1.16      {d4[]}, [r5]!
147 ;  VSRI.64      d4, d5, #16
148 ;  ; Load last *x
149 ;  VLD1.16      {d6[]}, [r4]!
150 ;  VMLAL.S16    q0, d4, d6
151 ;  MOV          pc, lr
152 ;  ENDP
153
154 ;; opus_val32 celt_pitch_xcorr_neon(opus_val16 *_x, opus_val16 *_y,
155 ;;  opus_val32 *xcorr, int len, int max_pitch)
156 ;celt_pitch_xcorr_neon PROC
157 ;  ; input:
158 ;  ;   r0  = opus_val16 *_x
159 ;  ;   r1  = opus_val16 *_y
160 ;  ;   r2  = opus_val32 *xcorr
161 ;  ;   r3  = int         len
162 ;  ; output:
163 ;  ;   r0  = int         maxcorr
164 ;  ; internal usage:
165 ;  ;   r4  = opus_val16 *x (for xcorr_kernel_neon())
166 ;  ;   r5  = opus_val16 *y (for xcorr_kernel_neon())
167 ;  ;   r6  = int         max_pitch
168 ;  ;   r12 = int         j
169 ;  ;   q15 = int         maxcorr[4] (q15 is not used by xcorr_kernel_neon())
170 ;  STMFD        sp!, {r4-r6, lr}
171 ;  LDR          r6, [sp, #16]
172 ;  VMOV.S32     q15, #1
173 ;  ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done
174 ;  SUBS         r6, r6, #4
175 ;  BLT celt_pitch_xcorr_neon_process4_done
176 ;celt_pitch_xcorr_neon_process4
177 ;  ; xcorr_kernel_neon parameters:
178 ;  ; r3 = len, r4 = _x, r5 = _y, q0 = {0, 0, 0, 0}
179 ;  MOV          r4, r0
180 ;  MOV          r5, r1
181 ;  VEOR         q0, q0, q0
182 ;  ; xcorr_kernel_neon only modifies r4, r5, r12, and q0...q3.
183 ;  ; So we don't save/restore any other registers.
184 ;  BL xcorr_kernel_neon
185 ;  SUBS         r6, r6, #4
186 ;  VST1.32      {q0}, [r2]!
187 ;  ; _y += 4
188 ;  ADD          r1, r1, #8
189 ;  VMAX.S32     q15, q15, q0
190 ;  ; if (max_pitch < 4) goto celt_pitch_xcorr_neon_process4_done
191 ;  BGE celt_pitch_xcorr_neon_process4
192 ;; We have less than 4 sums left to compute.
193 ;celt_pitch_xcorr_neon_process4_done
194 ;  ADDS         r6, r6, #4
195 ;  ; Reduce maxcorr to a single value
196 ;  VMAX.S32     d30, d30, d31
197 ;  VPMAX.S32    d30, d30, d30
198 ;  ; if (max_pitch <= 0) goto celt_pitch_xcorr_neon_done
199 ;  BLE celt_pitch_xcorr_neon_done
200 ;; Now compute each remaining sum one at a time.
201 ;celt_pitch_xcorr_neon_process_remaining
202 ;  MOV          r4, r0
203 ;  MOV          r5, r1
204 ;  VMOV.I32     q0, #0
205 ;  SUBS         r12, r3, #8
206 ;  BLT celt_pitch_xcorr_neon_process_remaining4
207 ;; Sum terms 8 at a time.
208 ;celt_pitch_xcorr_neon_process_remaining_loop8
209 ;  ; Load x[0...7]
210 ;  VLD1.16      {q1}, [r4]!
211 ;  ; Load y[0...7]
212 ;  VLD1.16      {q2}, [r5]!
213 ;  SUBS         r12, r12, #8
214 ;  VMLAL.S16    q0, d4, d2
215 ;  VMLAL.S16    q0, d5, d3
216 ;  BGE celt_pitch_xcorr_neon_process_remaining_loop8
217 ;; Sum terms 4 at a time.
218 ;celt_pitch_xcorr_neon_process_remaining4
219 ;  ADDS         r12, r12, #4
220 ;  BLT celt_pitch_xcorr_neon_process_remaining4_done
221 ;  ; Load x[0...3]
222 ;  VLD1.16      {d2}, [r4]!
223 ;  ; Load y[0...3]
224 ;  VLD1.16      {d3}, [r5]!
225 ;  SUB          r12, r12, #4
226 ;  VMLAL.S16    q0, d3, d2
227 ;  ; Reduce the sum to a single value.
228 ;  VADD.S32     d0, d0, d1
229 ;  VPADDL.S32   d0, d0
230 ;celt_pitch_xcorr_neon_process_remaining4_done
231 ;  ADDS         r12, r12, #4
232 ;  BLE celt_pitch_xcorr_neon_process_remaining_loop_done
233 ;; Sum terms 1 at a time.
234 ;celt_pitch_xcorr_neon_process_remaining_loop1
235 ;  VLD1.16      {d2[]}, [r4]!
236 ;  VLD1.16      {d3[]}, [r5]!
237 ;  SUBS         r12, r12, #1
238 ;  VMLAL.S16    q0, d2, d3
239 ;  BGT celt_pitch_xcorr_neon_process_remaining_loop1
240 ;celt_pitch_xcorr_neon_process_remaining_loop_done
241 ;  VST1.32      {d0[0]}, [r2]!
242 ;  VMAX.S32     d30, d30, d0
243 ;  SUBS         r6, r6, #1
244 ;  ; _y++
245 ;  ADD          r1, r1, #2
246 ;  ; if (--max_pitch > 0) goto celt_pitch_xcorr_neon_process_remaining
247 ;  BGT celt_pitch_xcorr_neon_process_remaining
248 ;celt_pitch_xcorr_neon_done
249 ;  VMOV.32      r0, d30[0]
250 ;  LDMFD        sp!, {r4-r6, pc}
251 ;  ENDP
252
253 xcorr_kernel_neon PROC
254   ; input:
255   ; r0 = opus_val16 *x
256   ; r1 = opus_val16 *y
257   ; r2 = int        len
258   ; q0 = opus_val32 sum (sum[3] | sum[2] | sum[1] | sum[0])
259
260   ; output:
261   ; q0 = sum
262
263   ; internal usage:
264   ; r3 = j
265   ; d2 = x_3|x_2|x_1|x_0  d3 = y_3|y_2|y_1|y_0
266   ; d4 = y_7|y_6|y_5|y_4  d5 = y_4|y_3|y_2|y_1
267   ; d6 = y_5|y_4|y_3|y_2  d7 = y_6|y_5|y_4|y_3
268   ; We will build d5, d6 and d7 vector from d3 and d4
269
270
271   VLD1.16   {d3}, [r1]!      ; Load y[3] downto y[0] to d3 lane (yy0)
272   SUB       r3, r2, #1
273   MOVS      r3, r3, lsr #2   ; j=(len-1)>>2
274   BEQ       xcorr_kernel_neon_process4_done
275
276   ; Process 4 x samples at a time
277   ; For this, we will need 4 y vectors
278 xcorr_kernel_neon_process4
279   SUBS      r3, r3, #1       ; j--
280   VLD1.16   d4, [r1]!        ; Load y[7] downto y[4] to d4 lane
281   VLD1.16   d2, [r0]!        ; Load x[3] downto x[0] to d2 lane
282   VEXT.16   d5, d3, d4, #1   ; Build y[4] downto y[1] vector (yy1)
283   VEXT.16   d6, d3, d4, #2   ; Build y[5] downto y[2] vector (yy2)
284   VEXT.16   d7, d3, d4, #3   ; Build y[6] downto y[3] vector (yy3)
285
286   VMLAL.S16 q0, d3, d2[0]    ; MAC16_16(sum, x[0], yy0)
287   VMLAL.S16 q0, d5, d2[1]    ; MAC16_16(sum, x[1], yy1)
288   VMLAL.S16 q0, d6, d2[2]    ; MAC16_16(sum, x[2], yy2)
289   VMLAL.S16 q0, d7, d2[3]    ; MAC16_16(sum, x[3], yy3)
290
291   VMOV.S16  d3, d4           ; Next y vector should be in d3 (yy0)
292
293   BNE xcorr_kernel_neon_process4
294
295 xcorr_kernel_neon_process4_done
296   ;Process len-1 to len
297   VLD1.16   {d2[]}, [r0]!    ; Load *x and duplicate to d2 lane
298
299   SUB       r3, r2, #1
300   ANDS      r3, r3, #3       ; j=(len-1)&3
301   VMLAL.S16 q0, d3, d2       ; MAC16_16(sum, *x, yy0)
302   BEQ xcorr_kernel_neon_done
303
304 xcorr_kernel_neon_process_remaining
305   SUBS      r3, r3, #1       ; j--
306   VLD1.16   {d4[]}, [r1]!    ; Load y value and duplicate to d4 lane
307   VLD1.16   {d2[]}, [r0]!    ; Load *x and duplicate to d2 lane
308   VEXT.16   d3, d3, d4, #1   ; Build y vector from previous and d4
309   VMLAL.S16 q0, d3, d2       ; MAC16_16(sum, *x, yy0)
310   BNE xcorr_kernel_neon_process_remaining
311
312 xcorr_kernel_neon_done
313   MOV       pc, lr
314   ENDP
315
316 celt_pitch_xcorr_neon PROC
317   ; input:
318   ; r0 = opus_val16 *_x
319   ; r1 = opus_val16 *_y
320   ; r2 = opus_val32 *xcorr
321   ; r3 = int        len
322
323   ; output:
324   ; r0 = maxcorr
325
326   STMFD     sp!, {r4-r9, lr}
327
328   LDR       r4, [sp, #28]        ; r4 = int max_pitch
329   MOV       r5, r0               ; r5 = _x
330   MOV       r6, r1               ; r6 = _y
331   MOV       r7, r2               ; r7 = xcorr
332   MOV       r2, r3               ; r2 = len
333
334   VMOV.S32  d16, #1              ; d16 = {1, 1}  (not used by xcorr_kernel_neon)
335   MOV       r8, #0               ; r8 = i = 0
336   CMP       r4, #3               ; max_pitch-3 <= 0  ---> pitch_xcorr_neon_process4_done
337   BLE       celt_pitch_xcorr_neon_process4_done
338
339   SUB       r9, r4, #3           ; r9 = max_pitch-3
340
341 celt_pitch_xcorr_neon_process4
342   MOV       r0, r5               ; r0 = _x
343   ADD       r1, r6 ,r8, LSL #1   ; r1 = _y + i
344   VMOV.I32  q0, #0               ; q0 = opus_val32 sum[4] = {0, 0, 0, 0}
345
346                                  ; xcorr_kernel_neon don't touch r2 (len)
347                                  ; So we don't store it
348   BL xcorr_kernel_neon           ; xcorr_kernel_neon(_x, _y+i, sum, len)
349
350   VST1.32   {q0}, [r7]!          ; Store sum to xcorr
351   VPMAX.S32 d0, d0, d1           ; d0 = max(sum[3], sum[2]) | max(sum[1], sum[0])
352   ADD       r8, r8, #4           ; i+=4
353   VPMAX.S32 d0, d0, d0           ; d0 = max(sum[3], sum[2], sum[1], sum[0])
354   CMP       r8, r9               ; i < max_pitch-3 ----> pitch_xcorr_neon_process4
355   VMAX.S32  d16, d16, d0         ; d16 = maxcorr = max(maxcorr, sum)
356
357   BLT       celt_pitch_xcorr_neon_process4
358
359 celt_pitch_xcorr_neon_process4_done
360   CMP       r8, r4;
361   BGE       celt_pitch_xcorr_neon_done
362
363 celt_pitch_xcorr_neon_process_remaining
364   MOV       r0, r5               ; r0 = _x
365   ADD       r1, r6, r8, LSL #1   ; r1 = _y + i
366   VMOV.I32  q0, #0
367   MOVS      r3, r2, LSR #2       ; r3 = j = len
368   BEQ       inner_loop_neon_process4_done
369
370 inner_loop_neon_process4
371   VLD1.16   {d2}, [r0]!          ; Load x
372   VLD1.16   {d3}, [r1]!          ; Load y
373   SUBS      r3, r3, #1
374   VMLAL.S16 q0, d2, d3
375   BNE       inner_loop_neon_process4
376
377   VPADD.S32 d0, d0, d1          ; Reduce sum
378   VPADD.S32 d0, d0, d0
379
380 inner_loop_neon_process4_done
381   ANDS      r3, r2, #3
382   BEQ       inner_loop_neon_done
383
384 inner_loop_neon_process_remaining
385   VLD1.16   {d2[]}, [r0]!
386   VLD1.16   {d3[]}, [r1]!
387   SUBS      r3, r3, #1
388   VMLAL.S16 q0, d2, d3
389   BNE       inner_loop_neon_process_remaining
390
391 inner_loop_neon_done
392   VST1.32   {d0[0]}, [r7]!
393   VMAX.S32  d16, d16, d0
394
395   ADD       r8, r8, #1
396   CMP       r8, r4
397   BCC       celt_pitch_xcorr_neon_process_remaining
398
399 celt_pitch_xcorr_neon_done
400   VMOV      d0, d16
401   VMOV.32   r0, d0[0]
402   LDMFD     sp!, {r4-r9, pc}
403   ENDP
404
405
406 ENDIF
407
408 IF OPUS_ARM_MAY_HAVE_EDSP
409
410 ; This will get used on ARMv7 devices without NEON, so it has been optimized
411 ; to take advantage of dual-issuing where possible.
412 xcorr_kernel_edsp PROC
413   ; input:
414   ;   r3      = int         len
415   ;   r4      = opus_val16 *_x (must be 32-bit aligned)
416   ;   r5      = opus_val16 *_y (must be 32-bit aligned)
417   ;   r6...r9 = opus_val32  sum[4]
418   ; output:
419   ;   r6...r9 = opus_val32  sum[4]
420   ; preserved: r0-r5
421   ; internal usage
422   ;   r2      = int         j
423   ;   r12,r14 = opus_val16  x[4]
424   ;   r10,r11 = opus_val16  y[4]
425   STMFD        sp!, {r2,r4,r5,lr}
426   LDR          r10, [r5], #4      ; Load y[0...1]
427   SUBS         r2, r3, #4         ; j = len-4
428   LDR          r11, [r5], #4      ; Load y[2...3]
429   BLE xcorr_kernel_edsp_process4_done
430   LDR          r12, [r4], #4      ; Load x[0...1]
431   ; Stall
432 xcorr_kernel_edsp_process4
433   ; The multiplies must issue from pipeline 0, and can't dual-issue with each
434   ; other. Every other instruction here dual-issues with a multiply, and is
435   ; thus "free". There should be no stalls in the body of the loop.
436   SMLABB       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x_0,y_0)
437   LDR          r14, [r4], #4      ; Load x[2...3]
438   SMLABT       r7, r12, r10, r7   ; sum[1] = MAC16_16(sum[1],x_0,y_1)
439   SUBS         r2, r2, #4         ; j-=4
440   SMLABB       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x_0,y_2)
441   SMLABT       r9, r12, r11, r9   ; sum[3] = MAC16_16(sum[3],x_0,y_3)
442   SMLATT       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x_1,y_1)
443   LDR          r10, [r5], #4      ; Load y[4...5]
444   SMLATB       r7, r12, r11, r7   ; sum[1] = MAC16_16(sum[1],x_1,y_2)
445   SMLATT       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x_1,y_3)
446   SMLATB       r9, r12, r10, r9   ; sum[3] = MAC16_16(sum[3],x_1,y_4)
447   LDRGT        r12, [r4], #4      ; Load x[0...1]
448   SMLABB       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],x_2,y_2)
449   SMLABT       r7, r14, r11, r7   ; sum[1] = MAC16_16(sum[1],x_2,y_3)
450   SMLABB       r8, r14, r10, r8   ; sum[2] = MAC16_16(sum[2],x_2,y_4)
451   SMLABT       r9, r14, r10, r9   ; sum[3] = MAC16_16(sum[3],x_2,y_5)
452   SMLATT       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],x_3,y_3)
453   LDR          r11, [r5], #4      ; Load y[6...7]
454   SMLATB       r7, r14, r10, r7   ; sum[1] = MAC16_16(sum[1],x_3,y_4)
455   SMLATT       r8, r14, r10, r8   ; sum[2] = MAC16_16(sum[2],x_3,y_5)
456   SMLATB       r9, r14, r11, r9   ; sum[3] = MAC16_16(sum[3],x_3,y_6)
457   BGT xcorr_kernel_edsp_process4
458 xcorr_kernel_edsp_process4_done
459   ADDS         r2, r2, #4
460   BLE xcorr_kernel_edsp_done
461   LDRH         r12, [r4], #2      ; r12 = *x++
462   SUBS         r2, r2, #1         ; j--
463   ; Stall
464   SMLABB       r6, r12, r10, r6   ; sum[0] = MAC16_16(sum[0],x,y_0)
465   LDRGTH       r14, [r4], #2      ; r14 = *x++
466   SMLABT       r7, r12, r10, r7   ; sum[1] = MAC16_16(sum[1],x,y_1)
467   SMLABB       r8, r12, r11, r8   ; sum[2] = MAC16_16(sum[2],x,y_2)
468   SMLABT       r9, r12, r11, r9   ; sum[3] = MAC16_16(sum[3],x,y_3)
469   BLE xcorr_kernel_edsp_done
470   SMLABT       r6, r14, r10, r6   ; sum[0] = MAC16_16(sum[0],x,y_1)
471   SUBS         r2, r2, #1         ; j--
472   SMLABB       r7, r14, r11, r7   ; sum[1] = MAC16_16(sum[1],x,y_2)
473   LDRH         r10, [r5], #2      ; r10 = y_4 = *y++
474   SMLABT       r8, r14, r11, r8   ; sum[2] = MAC16_16(sum[2],x,y_3)
475   LDRGTH       r12, [r4], #2      ; r12 = *x++
476   SMLABB       r9, r14, r10, r9   ; sum[3] = MAC16_16(sum[3],x,y_4)
477   BLE xcorr_kernel_edsp_done
478   SMLABB       r6, r12, r11, r6   ; sum[0] = MAC16_16(sum[0],tmp,y_2)
479   CMP          r2, #1             ; j--
480   SMLABT       r7, r12, r11, r7   ; sum[1] = MAC16_16(sum[1],tmp,y_3)
481   LDRH         r2, [r5], #2       ; r2 = y_5 = *y++
482   SMLABB       r8, r12, r10, r8   ; sum[2] = MAC16_16(sum[2],tmp,y_4)
483   LDRGTH       r14, [r4]          ; r14 = *x
484   SMLABB       r9, r12, r2, r9    ; sum[3] = MAC16_16(sum[3],tmp,y_5)
485   BLE xcorr_kernel_edsp_done
486   SMLABT       r6, r14, r11, r6   ; sum[0] = MAC16_16(sum[0],tmp,y_3)
487   LDRH         r11, [r5]          ; r11 = y_6 = *y
488   SMLABB       r7, r14, r10, r7   ; sum[1] = MAC16_16(sum[1],tmp,y_4)
489   SMLABB       r8, r14, r2, r8    ; sum[2] = MAC16_16(sum[2],tmp,y_5)
490   SMLABB       r9, r14, r11, r9   ; sum[3] = MAC16_16(sum[3],tmp,y_6)
491 xcorr_kernel_edsp_done
492   LDMFD        sp!, {r2,r4,r5,pc}
493   ENDP
494
495 celt_pitch_xcorr_edsp PROC
496   ; input:
497   ;   r0  = opus_val16 *_x (must be 32-bit aligned)
498   ;   r1  = opus_val16 *_y (only needs to be 16-bit aligned)
499   ;   r2  = opus_val32 *xcorr
500   ;   r3  = int         len
501   ; output:
502   ;   r0  = maxcorr
503   ; internal usage
504   ;   r4  = opus_val16 *x
505   ;   r5  = opus_val16 *y
506   ;   r6  = opus_val32  sum0
507   ;   r7  = opus_val32  sum1
508   ;   r8  = opus_val32  sum2
509   ;   r9  = opus_val32  sum3
510   ;   r1  = int         max_pitch
511   ;   r12 = int         j
512   STMFD        sp!, {r4-r11, lr}
513   MOV          r5, r1
514   LDR          r1, [sp, #36]
515   MOV          r4, r0
516   TST          r5, #3
517   ; maxcorr = 1
518   MOV          r0, #1
519   BEQ          celt_pitch_xcorr_edsp_process1u_done
520 ; Compute one sum at the start to make y 32-bit aligned.
521   SUBS         r12, r3, #4
522   ; r14 = sum = 0
523   MOV          r14, #0
524   LDRH         r8, [r5], #2
525   BLE celt_pitch_xcorr_edsp_process1u_loop4_done
526   LDR          r6, [r4], #4
527   LDR          r9, [r5], #4
528   LDR          r7, [r4], #4
529 celt_pitch_xcorr_edsp_process1u_loop4
530   SMLABB       r14, r6, r8, r14     ; sum = MAC16_16(sum, x_0, y_0)
531   SUBS         r12, r12, #4         ; j-=4
532   SMLATB       r14, r6, r9, r14     ; sum = MAC16_16(sum, x_1, y_1)
533   LDR          r10, [r5], #4
534   SMLABT       r14, r7, r9, r14     ; sum = MAC16_16(sum, x_2, y_2)
535   LDRGT        r6, [r4], #4
536   SMLATB       r14, r7, r10, r14    ; sum = MAC16_16(sum, x_3, y_3)
537   LDRGT        r9, [r5], #4
538   MOV          r8, r10, LSR #16
539   LDRGT        r7, [r4], #4
540   BGT celt_pitch_xcorr_edsp_process1u_loop4
541 celt_pitch_xcorr_edsp_process1u_loop4_done
542   ADDS         r12, r12, #4
543 celt_pitch_xcorr_edsp_process1u_loop1
544   LDRGEH       r6, [r4], #2
545   ; Stall
546   SMLABBGE     r14, r6, r8, r14    ; sum = MAC16_16(sum, *x, *y)
547   SUBGES       r12, r12, #1
548   LDRGTH       r8, [r5], #2
549   BGT celt_pitch_xcorr_edsp_process1u_loop1
550   ; Restore _x
551   SUB          r4, r4, r3, LSL #1
552   ; Restore and advance _y
553   SUB          r5, r5, r3, LSL #1
554   ; maxcorr = max(maxcorr, sum)
555   CMP          r0, r14
556   ADD          r5, r5, #2
557   MOVLT        r0, r14
558   SUBS         r1, r1, #1
559   ; xcorr[i] = sum
560   STR          r14, [r2], #4
561   BLE celt_pitch_xcorr_edsp_done
562 celt_pitch_xcorr_edsp_process1u_done
563   ; if (max_pitch < 4) goto celt_pitch_xcorr_edsp_process2
564   SUBS         r1, r1, #4
565   BLT celt_pitch_xcorr_edsp_process2
566 celt_pitch_xcorr_edsp_process4
567   ; xcorr_kernel_edsp parameters:
568   ; r3 = len, r4 = _x, r5 = _y, r6...r9 = sum[4] = {0, 0, 0, 0}
569   MOV          r6, #0
570   MOV          r7, #0
571   MOV          r8, #0
572   MOV          r9, #0
573   BL xcorr_kernel_edsp  ; xcorr_kernel_edsp(_x, _y+i, xcorr+i, len)
574   ; maxcorr = max(maxcorr, sum0, sum1, sum2, sum3)
575   CMP          r0, r6
576   ; _y+=4
577   ADD          r5, r5, #8
578   MOVLT        r0, r6
579   CMP          r0, r7
580   MOVLT        r0, r7
581   CMP          r0, r8
582   MOVLT        r0, r8
583   CMP          r0, r9
584   MOVLT        r0, r9
585   STMIA        r2!, {r6-r9}
586   SUBS         r1, r1, #4
587   BGE celt_pitch_xcorr_edsp_process4
588 celt_pitch_xcorr_edsp_process2
589   ADDS         r1, r1, #2
590   BLT celt_pitch_xcorr_edsp_process1a
591   SUBS         r12, r3, #4
592   ; {r10, r11} = {sum0, sum1} = {0, 0}
593   MOV          r10, #0
594   MOV          r11, #0
595   LDR          r8, [r5], #4
596   BLE celt_pitch_xcorr_edsp_process2_loop_done
597   LDR          r6, [r4], #4
598   LDR          r9, [r5], #4
599 celt_pitch_xcorr_edsp_process2_loop4
600   SMLABB       r10, r6, r8, r10     ; sum0 = MAC16_16(sum0, x_0, y_0)
601   LDR          r7, [r4], #4
602   SMLABT       r11, r6, r8, r11     ; sum1 = MAC16_16(sum1, x_0, y_1)
603   SUBS         r12, r12, #4         ; j-=4
604   SMLATT       r10, r6, r8, r10     ; sum0 = MAC16_16(sum0, x_1, y_1)
605   LDR          r8, [r5], #4
606   SMLATB       r11, r6, r9, r11     ; sum1 = MAC16_16(sum1, x_1, y_2)
607   LDRGT        r6, [r4], #4
608   SMLABB       r10, r7, r9, r10     ; sum0 = MAC16_16(sum0, x_2, y_2)
609   SMLABT       r11, r7, r9, r11     ; sum1 = MAC16_16(sum1, x_2, y_3)
610   SMLATT       r10, r7, r9, r10     ; sum0 = MAC16_16(sum0, x_3, y_3)
611   LDRGT        r9, [r5], #4
612   SMLATB       r11, r7, r8, r11     ; sum1 = MAC16_16(sum1, x_3, y_4)
613   BGT celt_pitch_xcorr_edsp_process2_loop4
614 celt_pitch_xcorr_edsp_process2_loop_done
615   ADDS         r12, r12, #2
616   BLE  celt_pitch_xcorr_edsp_process2_1
617   LDR          r6, [r4], #4
618   ; Stall
619   SMLABB       r10, r6, r8, r10     ; sum0 = MAC16_16(sum0, x_0, y_0)
620   LDR          r9, [r5], #4
621   SMLABT       r11, r6, r8, r11     ; sum1 = MAC16_16(sum1, x_0, y_1)
622   SUB          r12, r12, #2
623   SMLATT       r10, r6, r8, r10     ; sum0 = MAC16_16(sum0, x_1, y_1)
624   MOV          r8, r9
625   SMLATB       r11, r6, r9, r11     ; sum1 = MAC16_16(sum1, x_1, y_2)
626 celt_pitch_xcorr_edsp_process2_1
627   LDRH         r6, [r4], #2
628   ADDS         r12, r12, #1
629   ; Stall
630   SMLABB       r10, r6, r8, r10     ; sum0 = MAC16_16(sum0, x_0, y_0)
631   LDRGTH       r7, [r4], #2
632   SMLABT       r11, r6, r8, r11     ; sum1 = MAC16_16(sum1, x_0, y_1)
633   BLE celt_pitch_xcorr_edsp_process2_done
634   LDRH         r9, [r5], #2
635   SMLABT       r10, r7, r8, r10     ; sum0 = MAC16_16(sum0, x_0, y_1)
636   SMLABB       r11, r7, r9, r11     ; sum1 = MAC16_16(sum1, x_0, y_2)
637 celt_pitch_xcorr_edsp_process2_done
638   ; Restore _x
639   SUB          r4, r4, r3, LSL #1
640   ; Restore and advance _y
641   SUB          r5, r5, r3, LSL #1
642   ; maxcorr = max(maxcorr, sum0)
643   CMP          r0, r10
644   ADD          r5, r5, #2
645   MOVLT        r0, r10
646   SUB          r1, r1, #2
647   ; maxcorr = max(maxcorr, sum1)
648   CMP          r0, r11
649   ; xcorr[i] = sum
650   STR          r10, [r2], #4
651   MOVLT        r0, r11
652   STR          r11, [r2], #4
653 celt_pitch_xcorr_edsp_process1a
654   ADDS         r1, r1, #1
655   BLT celt_pitch_xcorr_edsp_done
656   SUBS         r12, r3, #4
657   ; r14 = sum = 0
658   MOV          r14, #0
659   BLT celt_pitch_xcorr_edsp_process1a_loop_done
660   LDR          r6, [r4], #4
661   LDR          r8, [r5], #4
662   LDR          r7, [r4], #4
663   LDR          r9, [r5], #4
664 celt_pitch_xcorr_edsp_process1a_loop4
665   SMLABB       r14, r6, r8, r14     ; sum = MAC16_16(sum, x_0, y_0)
666   SUBS         r12, r12, #4         ; j-=4
667   SMLATT       r14, r6, r8, r14     ; sum = MAC16_16(sum, x_1, y_1)
668   LDRGE        r6, [r4], #4
669   SMLABB       r14, r7, r9, r14     ; sum = MAC16_16(sum, x_2, y_2)
670   LDRGE        r8, [r5], #4
671   SMLATT       r14, r7, r9, r14     ; sum = MAC16_16(sum, x_3, y_3)
672   LDRGE        r7, [r4], #4
673   LDRGE        r9, [r5], #4
674   BGE celt_pitch_xcorr_edsp_process1a_loop4
675 celt_pitch_xcorr_edsp_process1a_loop_done
676   ADDS         r12, r12, #2
677   LDRGE        r6, [r4], #4
678   LDRGE        r8, [r5], #4
679   ; Stall
680   SMLABBGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, x_0, y_0)
681   SUBGE        r12, r12, #2
682   SMLATTGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, x_1, y_1)
683   ADDS         r12, r12, #1
684   LDRGEH       r6, [r4], #2
685   LDRGEH       r8, [r5], #2
686   SMLABBGE     r14, r6, r8, r14     ; sum = MAC16_16(sum, *x, *y)
687   ; maxcorr = max(maxcorr, sum)
688   CMP          r0, r14
689   ; xcorr[i] = sum
690   STR          r14, [r2], #4
691   MOVLT        r0, r14
692 celt_pitch_xcorr_edsp_done
693   LDMFD        sp!, {r4-r11, pc}
694   ENDP
695
696 ENDIF
697
698 END