1;
2;  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
3;
4;  Use of this source code is governed by a BSD-style license
5;  that can be found in the LICENSE file in the root of the source
6;  tree. An additional intellectual property rights grant can be found
7;  in the file PATENTS.  All contributing project authors may
8;  be found in the AUTHORS file in the root of the source tree.
9;
10
11    EXPORT  |vpx_lpf_horizontal_8_neon|
12    EXPORT  |vpx_lpf_horizontal_8_dual_neon|
13    EXPORT  |vpx_lpf_vertical_8_neon|
14    EXPORT  |vpx_lpf_vertical_8_dual_neon|
15    ARM
16
17    AREA ||.text||, CODE, READONLY, ALIGN=2
18
19; Currently vpx only works on iterations 8 at a time. The vp8 loop filter
20; works on 16 iterations at a time.
21;
22; void vpx_lpf_horizontal_8_neon(uint8_t *s, int p,
23;                                const uint8_t *blimit,
24;                                const uint8_t *limit,
25;                                const uint8_t *thresh)
26; r0    uint8_t *s,
27; r1    int p, /* pitch */
28; r2    const uint8_t *blimit,
29; r3    const uint8_t *limit,
30; sp    const uint8_t *thresh,
31|vpx_lpf_horizontal_8_neon| PROC
32    push        {r4-r5, lr}
33
34    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
35    ldr         r2, [sp, #12]              ; load thresh
36    add         r1, r1, r1                 ; double pitch
37
38    vld1.8      {d1[]}, [r3]               ; duplicate *limit
39    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
40
41    sub         r3, r0, r1, lsl #1         ; move src pointer down by 4 lines
42    add         r2, r3, r1, lsr #1         ; set to 3 lines down
43
44    vld1.u8     {d3}, [r3@64], r1          ; p3
45    vld1.u8     {d4}, [r2@64], r1          ; p2
46    vld1.u8     {d5}, [r3@64], r1          ; p1
47    vld1.u8     {d6}, [r2@64], r1          ; p0
48    vld1.u8     {d7}, [r3@64], r1          ; q0
49    vld1.u8     {d16}, [r2@64], r1         ; q1
50    vld1.u8     {d17}, [r3@64]             ; q2
51    vld1.u8     {d18}, [r2@64], r1         ; q3
52
53    sub         r3, r3, r1, lsl #1
54    sub         r2, r2, r1, lsl #2
55
56    bl          vpx_mbloop_filter_neon
57
58    vst1.u8     {d0}, [r2@64], r1          ; store op2
59    vst1.u8     {d1}, [r3@64], r1          ; store op1
60    vst1.u8     {d2}, [r2@64], r1          ; store op0
61    vst1.u8     {d3}, [r3@64], r1          ; store oq0
62    vst1.u8     {d4}, [r2@64], r1          ; store oq1
63    vst1.u8     {d5}, [r3@64], r1          ; store oq2
64
65    pop         {r4-r5, pc}
66
67    ENDP        ; |vpx_lpf_horizontal_8_neon|
68
69;void vpx_lpf_horizontal_8_dual_neon(uint8_t *s,
70;                                    int p,
71;                                    const uint8_t *blimit0,
72;                                    const uint8_t *limit0,
73;                                    const uint8_t *thresh0,
74;                                    const uint8_t *blimit1,
75;                                    const uint8_t *limit1,
76;                                    const uint8_t *thresh1)
77; r0      uint8_t *s,
78; r1      int p, /* pitch */
79; r2      const uint8_t *blimit0,
80; r3      const uint8_t *limit0,
81; sp      const uint8_t *thresh0,
82; sp + 4  const uint8_t *blimit1,
83; sp + 8  const uint8_t *limit1,
84; sp + 12 const uint8_t *thresh1,
85|vpx_lpf_horizontal_8_dual_neon| PROC
86    push        {r0-r1, lr}
87    ldr         lr, [sp, #12]
88    push        {lr}                       ; thresh0
89    bl          vpx_lpf_horizontal_8_neon
90
91    ldr         r2, [sp, #20]              ; blimit1
92    ldr         r3, [sp, #24]              ; limit1
93    ldr         lr, [sp, #28]
94    str         lr, [sp, #16]              ; thresh1
95    add         sp, #4
96    pop         {r0-r1, lr}
97    add         r0, #8                     ; s + 8
98    b           vpx_lpf_horizontal_8_neon
99    ENDP        ; |vpx_lpf_horizontal_8_dual_neon|
100
101; void vpx_lpf_vertical_8_neon(uint8_t *s,
102;                              int pitch,
103;                              const uint8_t *blimit,
104;                              const uint8_t *limit,
105;                              const uint8_t *thresh)
106;
107; r0    uint8_t *s,
108; r1    int pitch,
109; r2    const uint8_t *blimit,
110; r3    const uint8_t *limit,
111; sp    const uint8_t *thresh,
112|vpx_lpf_vertical_8_neon| PROC
113    push        {r4-r5, lr}
114
115    vld1.8      {d0[]}, [r2]              ; duplicate *blimit
116    vld1.8      {d1[]}, [r3]              ; duplicate *limit
117
118    ldr         r3, [sp, #12]             ; load thresh
119    sub         r2, r0, #4                ; move s pointer down by 4 columns
120
121    vld1.8      {d2[]}, [r3]              ; duplicate *thresh
122
123    vld1.u8     {d3}, [r2], r1             ; load s data
124    vld1.u8     {d4}, [r2], r1
125    vld1.u8     {d5}, [r2], r1
126    vld1.u8     {d6}, [r2], r1
127    vld1.u8     {d7}, [r2], r1
128    vld1.u8     {d16}, [r2], r1
129    vld1.u8     {d17}, [r2], r1
130    vld1.u8     {d18}, [r2]
131
132    ;transpose to 8x16 matrix
133    vtrn.32     d3, d7
134    vtrn.32     d4, d16
135    vtrn.32     d5, d17
136    vtrn.32     d6, d18
137
138    vtrn.16     d3, d5
139    vtrn.16     d4, d6
140    vtrn.16     d7, d17
141    vtrn.16     d16, d18
142
143    vtrn.8      d3, d4
144    vtrn.8      d5, d6
145    vtrn.8      d7, d16
146    vtrn.8      d17, d18
147
148    sub         r2, r0, #3
149    add         r3, r0, #1
150
151    bl          vpx_mbloop_filter_neon
152
153    ;store op2, op1, op0, oq0
154    vst4.8      {d0[0], d1[0], d2[0], d3[0]}, [r2], r1
155    vst4.8      {d0[1], d1[1], d2[1], d3[1]}, [r2], r1
156    vst4.8      {d0[2], d1[2], d2[2], d3[2]}, [r2], r1
157    vst4.8      {d0[3], d1[3], d2[3], d3[3]}, [r2], r1
158    vst4.8      {d0[4], d1[4], d2[4], d3[4]}, [r2], r1
159    vst4.8      {d0[5], d1[5], d2[5], d3[5]}, [r2], r1
160    vst4.8      {d0[6], d1[6], d2[6], d3[6]}, [r2], r1
161    vst4.8      {d0[7], d1[7], d2[7], d3[7]}, [r2]
162
163    ;store oq1, oq2
164    vst2.8      {d4[0], d5[0]}, [r3], r1
165    vst2.8      {d4[1], d5[1]}, [r3], r1
166    vst2.8      {d4[2], d5[2]}, [r3], r1
167    vst2.8      {d4[3], d5[3]}, [r3], r1
168    vst2.8      {d4[4], d5[4]}, [r3], r1
169    vst2.8      {d4[5], d5[5]}, [r3], r1
170    vst2.8      {d4[6], d5[6]}, [r3], r1
171    vst2.8      {d4[7], d5[7]}, [r3]
172
173    pop         {r4-r5, pc}
174    ENDP        ; |vpx_lpf_vertical_8_neon|
175
176;void vpx_lpf_vertical_8_dual_neon(uint8_t *s,
177;                                  int pitch,
178;                                  const uint8_t *blimit0,
179;                                  const uint8_t *limit0,
180;                                  const uint8_t *thresh0,
181;                                  const uint8_t *blimit1,
182;                                  const uint8_t *limit1,
183;                                  const uint8_t *thresh1)
184; r0      uint8_t *s,
185; r1      int pitch
186; r2      const uint8_t *blimit0,
187; r3      const uint8_t *limit0,
188; sp      const uint8_t *thresh0,
189; sp + 4  const uint8_t *blimit1,
190; sp + 8  const uint8_t *limit1,
191; sp + 12 const uint8_t *thresh1,
192|vpx_lpf_vertical_8_dual_neon| PROC
193    push        {r0-r1, lr}
194    ldr         lr, [sp, #12]
195    push        {lr}                       ; thresh0
196    bl          vpx_lpf_vertical_8_neon
197
198    ldr         r2, [sp, #20]              ; blimit1
199    ldr         r3, [sp, #24]              ; limit1
200    ldr         lr, [sp, #28]
201    str         lr, [sp, #16]              ; thresh1
202    add         sp, #4
203    pop         {r0-r1, lr}
204    add         r0, r1, lsl #3             ; s + 8 * pitch
205    b           vpx_lpf_vertical_8_neon
206    ENDP        ; |vpx_lpf_vertical_8_dual_neon|
207
208; void vpx_mbloop_filter_neon();
209; This is a helper function for the loopfilters. The invidual functions do the
210; necessary load, transpose (if necessary) and store. The function does not use
211; registers d8-d15.
212;
213; Inputs:
214; r0-r3, r12 PRESERVE
215; d0    blimit
216; d1    limit
217; d2    thresh
218; d3    p3
219; d4    p2
220; d5    p1
221; d6    p0
222; d7    q0
223; d16   q1
224; d17   q2
225; d18   q3
226;
227; Outputs:
228; d0    op2
229; d1    op1
230; d2    op0
231; d3    oq0
232; d4    oq1
233; d5    oq2
234|vpx_mbloop_filter_neon| PROC
235    ; filter_mask
236    vabd.u8     d19, d3, d4                ; m1 = abs(p3 - p2)
237    vabd.u8     d20, d4, d5                ; m2 = abs(p2 - p1)
238    vabd.u8     d21, d5, d6                ; m3 = abs(p1 - p0)
239    vabd.u8     d22, d16, d7               ; m4 = abs(q1 - q0)
240    vabd.u8     d23, d17, d16              ; m5 = abs(q2 - q1)
241    vabd.u8     d24, d18, d17              ; m6 = abs(q3 - q2)
242
243    ; only compare the largest value to limit
244    vmax.u8     d19, d19, d20              ; m1 = max(m1, m2)
245    vmax.u8     d20, d21, d22              ; m2 = max(m3, m4)
246
247    vabd.u8     d25, d6, d4                ; m7 = abs(p0 - p2)
248
249    vmax.u8     d23, d23, d24              ; m3 = max(m5, m6)
250
251    vabd.u8     d26, d7, d17               ; m8 = abs(q0 - q2)
252
253    vmax.u8     d19, d19, d20
254
255    vabd.u8     d24, d6, d7                ; m9 = abs(p0 - q0)
256    vabd.u8     d27, d3, d6                ; m10 = abs(p3 - p0)
257    vabd.u8     d28, d18, d7               ; m11 = abs(q3 - q0)
258
259    vmax.u8     d19, d19, d23
260
261    vabd.u8     d23, d5, d16               ; a = abs(p1 - q1)
262    vqadd.u8    d24, d24, d24              ; b = abs(p0 - q0) * 2
263
264    ; abs () > limit
265    vcge.u8     d19, d1, d19
266
267    ; only compare the largest value to thresh
268    vmax.u8     d25, d25, d26              ; m4 = max(m7, m8)
269    vmax.u8     d26, d27, d28              ; m5 = max(m10, m11)
270
271    vshr.u8     d23, d23, #1               ; a = a / 2
272
273    vmax.u8     d25, d25, d26              ; m4 = max(m4, m5)
274
275    vqadd.u8    d24, d24, d23              ; a = b + a
276
277    vmax.u8     d20, d20, d25              ; m2 = max(m2, m4)
278
279    vmov.u8     d23, #1
280    vcge.u8     d24, d0, d24               ; a > blimit
281
282    vcgt.u8     d21, d21, d2               ; (abs(p1 - p0) > thresh)*-1
283
284    vcge.u8     d20, d23, d20              ; flat
285
286    vand        d19, d19, d24              ; mask
287
288    vcgt.u8     d23, d22, d2               ; (abs(q1 - q0) > thresh)*-1
289
290    vand        d20, d20, d19              ; flat & mask
291
292    vmov.u8     d22, #0x80
293
294    vorr        d23, d21, d23              ; hev
295
296    ; This instruction will truncate the "flat & mask" masks down to 4 bits
297    ; each to fit into one 32 bit arm register. The values are stored in
298    ; q10.64[0].
299    vshrn.u16   d30, q10, #4
300    vmov.u32    r4, d30[0]                 ; flat & mask 4bits
301
302    adds        r5, r4, #1                 ; Check for all 1's
303
304    ; If mask and flat are 1's for all vectors, then we only need to execute
305    ; the power branch for all vectors.
306    beq         power_branch_only
307
308    cmp         r4, #0                     ; Check for 0, set flag for later
309
310    ; mbfilter() function
311    ; filter() function
312    ; convert to signed
313    veor        d21, d7, d22               ; qs0
314    veor        d24, d6, d22               ; ps0
315    veor        d25, d5, d22               ; ps1
316    veor        d26, d16, d22              ; qs1
317
318    vmov.u8     d27, #3
319
320    vsub.s8     d28, d21, d24              ; ( qs0 - ps0)
321
322    vqsub.s8    d29, d25, d26              ; filter = clamp(ps1-qs1)
323
324    vmull.s8    q15, d28, d27              ; 3 * ( qs0 - ps0)
325
326    vand        d29, d29, d23              ; filter &= hev
327
328    vaddw.s8    q15, q15, d29              ; filter + 3 * (qs0 - ps0)
329
330    vmov.u8     d29, #4
331
332    ; filter = clamp(filter + 3 * ( qs0 - ps0))
333    vqmovn.s16  d28, q15
334
335    vand        d28, d28, d19              ; filter &= mask
336
337    vqadd.s8    d30, d28, d27              ; filter2 = clamp(filter+3)
338    vqadd.s8    d29, d28, d29              ; filter1 = clamp(filter+4)
339    vshr.s8     d30, d30, #3               ; filter2 >>= 3
340    vshr.s8     d29, d29, #3               ; filter1 >>= 3
341
342    vqadd.s8    d24, d24, d30              ; op0 = clamp(ps0 + filter2)
343    vqsub.s8    d21, d21, d29              ; oq0 = clamp(qs0 - filter1)
344
345    ; outer tap adjustments: ++filter1 >> 1
346    vrshr.s8    d29, d29, #1
347    vbic        d29, d29, d23              ; filter &= ~hev
348
349    vqadd.s8    d25, d25, d29              ; op1 = clamp(ps1 + filter)
350    vqsub.s8    d26, d26, d29              ; oq1 = clamp(qs1 - filter)
351
352    ; If mask and flat are 0's for all vectors, then we only need to execute
353    ; the filter branch for all vectors.
354    beq         filter_branch_only
355
356    ; If mask and flat are mixed then we must perform both branches and
357    ; combine the data.
358    veor        d24, d24, d22              ; *f_op0 = u^0x80
359    veor        d21, d21, d22              ; *f_oq0 = u^0x80
360    veor        d25, d25, d22              ; *f_op1 = u^0x80
361    veor        d26, d26, d22              ; *f_oq1 = u^0x80
362
363    ; At this point we have already executed the filter branch. The filter
364    ; branch does not set op2 or oq2, so use p2 and q2. Execute the power
365    ; branch and combine the data.
366    vmov.u8     d23, #2
367    vaddl.u8    q14, d6, d7                ; r_op2 = p0 + q0
368    vmlal.u8    q14, d3, d27               ; r_op2 += p3 * 3
369    vmlal.u8    q14, d4, d23               ; r_op2 += p2 * 2
370
371    vbif        d0, d4, d20                ; op2 |= p2 & ~(flat & mask)
372
373    vaddw.u8    q14, d5                    ; r_op2 += p1
374
375    vbif        d1, d25, d20               ; op1 |= f_op1 & ~(flat & mask)
376
377    vqrshrn.u16 d30, q14, #3               ; r_op2
378
379    vsubw.u8    q14, d3                    ; r_op1 = r_op2 - p3
380    vsubw.u8    q14, d4                    ; r_op1 -= p2
381    vaddw.u8    q14, d5                    ; r_op1 += p1
382    vaddw.u8    q14, d16                   ; r_op1 += q1
383
384    vbif        d2, d24, d20               ; op0 |= f_op0 & ~(flat & mask)
385
386    vqrshrn.u16 d31, q14, #3               ; r_op1
387
388    vsubw.u8    q14, d3                    ; r_op0 = r_op1 - p3
389    vsubw.u8    q14, d5                    ; r_op0 -= p1
390    vaddw.u8    q14, d6                    ; r_op0 += p0
391    vaddw.u8    q14, d17                   ; r_op0 += q2
392
393    vbit        d0, d30, d20               ; op2 |= r_op2 & (flat & mask)
394
395    vqrshrn.u16 d23, q14, #3               ; r_op0
396
397    vsubw.u8    q14, d3                    ; r_oq0 = r_op0 - p3
398    vsubw.u8    q14, d6                    ; r_oq0 -= p0
399    vaddw.u8    q14, d7                    ; r_oq0 += q0
400
401    vbit        d1, d31, d20               ; op1 |= r_op1 & (flat & mask)
402
403    vaddw.u8    q14, d18                   ; oq0 += q3
404
405    vbit        d2, d23, d20               ; op0 |= r_op0 & (flat & mask)
406
407    vqrshrn.u16 d22, q14, #3               ; r_oq0
408
409    vsubw.u8    q14, d4                    ; r_oq1 = r_oq0 - p2
410    vsubw.u8    q14, d7                    ; r_oq1 -= q0
411    vaddw.u8    q14, d16                   ; r_oq1 += q1
412
413    vbif        d3, d21, d20               ; oq0 |= f_oq0 & ~(flat & mask)
414
415    vaddw.u8    q14, d18                   ; r_oq1 += q3
416
417    vbif        d4, d26, d20               ; oq1 |= f_oq1 & ~(flat & mask)
418
419    vqrshrn.u16 d6, q14, #3                ; r_oq1
420
421    vsubw.u8    q14, d5                    ; r_oq2 = r_oq1 - p1
422    vsubw.u8    q14, d16                   ; r_oq2 -= q1
423    vaddw.u8    q14, d17                   ; r_oq2 += q2
424    vaddw.u8    q14, d18                   ; r_oq2 += q3
425
426    vbif        d5, d17, d20               ; oq2 |= q2 & ~(flat & mask)
427
428    vqrshrn.u16 d7, q14, #3                ; r_oq2
429
430    vbit        d3, d22, d20               ; oq0 |= r_oq0 & (flat & mask)
431    vbit        d4, d6, d20                ; oq1 |= r_oq1 & (flat & mask)
432    vbit        d5, d7, d20                ; oq2 |= r_oq2 & (flat & mask)
433
434    bx          lr
435
436power_branch_only
437    vmov.u8     d27, #3
438    vmov.u8     d21, #2
439    vaddl.u8    q14, d6, d7                ; op2 = p0 + q0
440    vmlal.u8    q14, d3, d27               ; op2 += p3 * 3
441    vmlal.u8    q14, d4, d21               ; op2 += p2 * 2
442    vaddw.u8    q14, d5                    ; op2 += p1
443    vqrshrn.u16 d0, q14, #3                ; op2
444
445    vsubw.u8    q14, d3                    ; op1 = op2 - p3
446    vsubw.u8    q14, d4                    ; op1 -= p2
447    vaddw.u8    q14, d5                    ; op1 += p1
448    vaddw.u8    q14, d16                   ; op1 += q1
449    vqrshrn.u16 d1, q14, #3                ; op1
450
451    vsubw.u8    q14, d3                    ; op0 = op1 - p3
452    vsubw.u8    q14, d5                    ; op0 -= p1
453    vaddw.u8    q14, d6                    ; op0 += p0
454    vaddw.u8    q14, d17                   ; op0 += q2
455    vqrshrn.u16 d2, q14, #3                ; op0
456
457    vsubw.u8    q14, d3                    ; oq0 = op0 - p3
458    vsubw.u8    q14, d6                    ; oq0 -= p0
459    vaddw.u8    q14, d7                    ; oq0 += q0
460    vaddw.u8    q14, d18                   ; oq0 += q3
461    vqrshrn.u16 d3, q14, #3                ; oq0
462
463    vsubw.u8    q14, d4                    ; oq1 = oq0 - p2
464    vsubw.u8    q14, d7                    ; oq1 -= q0
465    vaddw.u8    q14, d16                   ; oq1 += q1
466    vaddw.u8    q14, d18                   ; oq1 += q3
467    vqrshrn.u16 d4, q14, #3                ; oq1
468
469    vsubw.u8    q14, d5                    ; oq2 = oq1 - p1
470    vsubw.u8    q14, d16                   ; oq2 -= q1
471    vaddw.u8    q14, d17                   ; oq2 += q2
472    vaddw.u8    q14, d18                   ; oq2 += q3
473    vqrshrn.u16 d5, q14, #3                ; oq2
474
475    bx          lr
476
477filter_branch_only
478    ; TODO(fgalligan): See if we can rearange registers so we do not need to
479    ; do the 2 vswp.
480    vswp        d0, d4                      ; op2
481    vswp        d5, d17                     ; oq2
482    veor        d2, d24, d22                ; *op0 = u^0x80
483    veor        d3, d21, d22                ; *oq0 = u^0x80
484    veor        d1, d25, d22                ; *op1 = u^0x80
485    veor        d4, d26, d22                ; *oq1 = u^0x80
486
487    bx          lr
488
489    ENDP        ; |vpx_mbloop_filter_neon|
490
491    END
492