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  |vp9_lpf_horizontal_4_neon|
12    EXPORT  |vp9_lpf_vertical_4_neon|
13    EXPORT  |vp9_lpf_horizontal_8_neon|
14    EXPORT  |vp9_lpf_vertical_8_neon|
15    ARM
16
17    AREA ||.text||, CODE, READONLY, ALIGN=2
18
19; Currently vp9 only works on iterations 8 at a time. The vp8 loop filter
20; works on 16 iterations at a time.
21; TODO(fgalligan): See about removing the count code as this function is only
22; called with a count of 1.
23;
24; void vp9_lpf_horizontal_4_neon(uint8_t *s,
25;                                int p /* pitch */,
26;                                const uint8_t *blimit,
27;                                const uint8_t *limit,
28;                                const uint8_t *thresh,
29;                                int count)
30;
31; r0    uint8_t *s,
32; r1    int p, /* pitch */
33; r2    const uint8_t *blimit,
34; r3    const uint8_t *limit,
35; sp    const uint8_t *thresh,
36; sp+4  int count
37|vp9_lpf_horizontal_4_neon| PROC
38    push        {lr}
39
40    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
41    ldr         r12, [sp, #8]              ; load count
42    ldr         r2, [sp, #4]               ; load thresh
43    add         r1, r1, r1                 ; double pitch
44
45    cmp         r12, #0
46    beq         end_vp9_lf_h_edge
47
48    vld1.8      {d1[]}, [r3]               ; duplicate *limit
49    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
50
51count_lf_h_loop
52    sub         r2, r0, r1, lsl #1         ; move src pointer down by 4 lines
53    add         r3, r2, r1, lsr #1         ; set to 3 lines down
54
55    vld1.u8     {d3}, [r2@64], r1          ; p3
56    vld1.u8     {d4}, [r3@64], r1          ; p2
57    vld1.u8     {d5}, [r2@64], r1          ; p1
58    vld1.u8     {d6}, [r3@64], r1          ; p0
59    vld1.u8     {d7}, [r2@64], r1          ; q0
60    vld1.u8     {d16}, [r3@64], r1         ; q1
61    vld1.u8     {d17}, [r2@64]             ; q2
62    vld1.u8     {d18}, [r3@64]             ; q3
63
64    sub         r2, r2, r1, lsl #1
65    sub         r3, r3, r1, lsl #1
66
67    bl          vp9_loop_filter_neon
68
69    vst1.u8     {d4}, [r2@64], r1          ; store op1
70    vst1.u8     {d5}, [r3@64], r1          ; store op0
71    vst1.u8     {d6}, [r2@64], r1          ; store oq0
72    vst1.u8     {d7}, [r3@64], r1          ; store oq1
73
74    add         r0, r0, #8
75    subs        r12, r12, #1
76    bne         count_lf_h_loop
77
78end_vp9_lf_h_edge
79    pop         {pc}
80    ENDP        ; |vp9_lpf_horizontal_4_neon|
81
82; Currently vp9 only works on iterations 8 at a time. The vp8 loop filter
83; works on 16 iterations at a time.
84; TODO(fgalligan): See about removing the count code as this function is only
85; called with a count of 1.
86;
87; void vp9_lpf_vertical_4_neon(uint8_t *s,
88;                              int p /* pitch */,
89;                              const uint8_t *blimit,
90;                              const uint8_t *limit,
91;                              const uint8_t *thresh,
92;                              int count)
93;
94; r0    uint8_t *s,
95; r1    int p, /* pitch */
96; r2    const uint8_t *blimit,
97; r3    const uint8_t *limit,
98; sp    const uint8_t *thresh,
99; sp+4  int count
100|vp9_lpf_vertical_4_neon| PROC
101    push        {lr}
102
103    vld1.8      {d0[]}, [r2]              ; duplicate *blimit
104    ldr         r12, [sp, #8]             ; load count
105    vld1.8      {d1[]}, [r3]              ; duplicate *limit
106
107    ldr         r3, [sp, #4]              ; load thresh
108    sub         r2, r0, #4                ; move s pointer down by 4 columns
109    cmp         r12, #0
110    beq         end_vp9_lf_v_edge
111
112    vld1.8      {d2[]}, [r3]              ; duplicate *thresh
113
114count_lf_v_loop
115    vld1.u8     {d3}, [r2], r1             ; load s data
116    vld1.u8     {d4}, [r2], r1
117    vld1.u8     {d5}, [r2], r1
118    vld1.u8     {d6}, [r2], r1
119    vld1.u8     {d7}, [r2], r1
120    vld1.u8     {d16}, [r2], r1
121    vld1.u8     {d17}, [r2], r1
122    vld1.u8     {d18}, [r2]
123
124    ;transpose to 8x16 matrix
125    vtrn.32     d3, d7
126    vtrn.32     d4, d16
127    vtrn.32     d5, d17
128    vtrn.32     d6, d18
129
130    vtrn.16     d3, d5
131    vtrn.16     d4, d6
132    vtrn.16     d7, d17
133    vtrn.16     d16, d18
134
135    vtrn.8      d3, d4
136    vtrn.8      d5, d6
137    vtrn.8      d7, d16
138    vtrn.8      d17, d18
139
140    bl          vp9_loop_filter_neon
141
142    sub         r0, r0, #2
143
144    ;store op1, op0, oq0, oq1
145    vst4.8      {d4[0], d5[0], d6[0], d7[0]}, [r0], r1
146    vst4.8      {d4[1], d5[1], d6[1], d7[1]}, [r0], r1
147    vst4.8      {d4[2], d5[2], d6[2], d7[2]}, [r0], r1
148    vst4.8      {d4[3], d5[3], d6[3], d7[3]}, [r0], r1
149    vst4.8      {d4[4], d5[4], d6[4], d7[4]}, [r0], r1
150    vst4.8      {d4[5], d5[5], d6[5], d7[5]}, [r0], r1
151    vst4.8      {d4[6], d5[6], d6[6], d7[6]}, [r0], r1
152    vst4.8      {d4[7], d5[7], d6[7], d7[7]}, [r0]
153
154    add         r0, r0, r1, lsl #3         ; s += pitch * 8
155    subs        r12, r12, #1
156    subne       r2, r0, #4                 ; move s pointer down by 4 columns
157    bne         count_lf_v_loop
158
159end_vp9_lf_v_edge
160    pop         {pc}
161    ENDP        ; |vp9_lpf_vertical_4_neon|
162
163; void vp9_loop_filter_neon();
164; This is a helper function for the loopfilters. The invidual functions do the
165; necessary load, transpose (if necessary) and store. The function does not use
166; registers d8-d15.
167;
168; Inputs:
169; r0-r3, r12 PRESERVE
170; d0    blimit
171; d1    limit
172; d2    thresh
173; d3    p3
174; d4    p2
175; d5    p1
176; d6    p0
177; d7    q0
178; d16   q1
179; d17   q2
180; d18   q3
181;
182; Outputs:
183; d4    op1
184; d5    op0
185; d6    oq0
186; d7    oq1
187|vp9_loop_filter_neon| PROC
188    ; filter_mask
189    vabd.u8     d19, d3, d4                 ; m1 = abs(p3 - p2)
190    vabd.u8     d20, d4, d5                 ; m2 = abs(p2 - p1)
191    vabd.u8     d21, d5, d6                 ; m3 = abs(p1 - p0)
192    vabd.u8     d22, d16, d7                ; m4 = abs(q1 - q0)
193    vabd.u8     d3, d17, d16                ; m5 = abs(q2 - q1)
194    vabd.u8     d4, d18, d17                ; m6 = abs(q3 - q2)
195
196    ; only compare the largest value to limit
197    vmax.u8     d19, d19, d20               ; m1 = max(m1, m2)
198    vmax.u8     d20, d21, d22               ; m2 = max(m3, m4)
199
200    vabd.u8     d17, d6, d7                 ; abs(p0 - q0)
201
202    vmax.u8     d3, d3, d4                  ; m3 = max(m5, m6)
203
204    vmov.u8     d18, #0x80
205
206    vmax.u8     d23, d19, d20               ; m1 = max(m1, m2)
207
208    ; hevmask
209    vcgt.u8     d21, d21, d2                ; (abs(p1 - p0) > thresh)*-1
210    vcgt.u8     d22, d22, d2                ; (abs(q1 - q0) > thresh)*-1
211    vmax.u8     d23, d23, d3                ; m1 = max(m1, m3)
212
213    vabd.u8     d28, d5, d16                ; a = abs(p1 - q1)
214    vqadd.u8    d17, d17, d17               ; b = abs(p0 - q0) * 2
215
216    veor        d7, d7, d18                 ; qs0
217
218    vcge.u8     d23, d1, d23                ; abs(m1) > limit
219
220    ; filter() function
221    ; convert to signed
222
223    vshr.u8     d28, d28, #1                ; a = a / 2
224    veor        d6, d6, d18                 ; ps0
225
226    veor        d5, d5, d18                 ; ps1
227    vqadd.u8    d17, d17, d28               ; a = b + a
228
229    veor        d16, d16, d18               ; qs1
230
231    vmov.u8     d19, #3
232
233    vsub.s8     d28, d7, d6                 ; ( qs0 - ps0)
234
235    vcge.u8     d17, d0, d17                ; a > blimit
236
237    vqsub.s8    d27, d5, d16                ; filter = clamp(ps1-qs1)
238    vorr        d22, d21, d22               ; hevmask
239
240    vmull.s8    q12, d28, d19               ; 3 * ( qs0 - ps0)
241
242    vand        d27, d27, d22               ; filter &= hev
243    vand        d23, d23, d17               ; filter_mask
244
245    vaddw.s8    q12, q12, d27               ; filter + 3 * (qs0 - ps0)
246
247    vmov.u8     d17, #4
248
249    ; filter = clamp(filter + 3 * ( qs0 - ps0))
250    vqmovn.s16  d27, q12
251
252    vand        d27, d27, d23               ; filter &= mask
253
254    vqadd.s8    d28, d27, d19               ; filter2 = clamp(filter+3)
255    vqadd.s8    d27, d27, d17               ; filter1 = clamp(filter+4)
256    vshr.s8     d28, d28, #3                ; filter2 >>= 3
257    vshr.s8     d27, d27, #3                ; filter1 >>= 3
258
259    vqadd.s8    d19, d6, d28                ; u = clamp(ps0 + filter2)
260    vqsub.s8    d26, d7, d27                ; u = clamp(qs0 - filter1)
261
262    ; outer tap adjustments
263    vrshr.s8    d27, d27, #1                ; filter = ++filter1 >> 1
264
265    veor        d6, d26, d18                ; *oq0 = u^0x80
266
267    vbic        d27, d27, d22               ; filter &= ~hev
268
269    vqadd.s8    d21, d5, d27                ; u = clamp(ps1 + filter)
270    vqsub.s8    d20, d16, d27               ; u = clamp(qs1 - filter)
271
272    veor        d5, d19, d18                ; *op0 = u^0x80
273    veor        d4, d21, d18                ; *op1 = u^0x80
274    veor        d7, d20, d18                ; *oq1 = u^0x80
275
276    bx          lr
277    ENDP        ; |vp9_loop_filter_neon|
278
279; void vp9_lpf_horizontal_8_neon(uint8_t *s, int p,
280;                                const uint8_t *blimit,
281;                                const uint8_t *limit,
282;                                const uint8_t *thresh,
283;                                int count)
284; r0    uint8_t *s,
285; r1    int p, /* pitch */
286; r2    const uint8_t *blimit,
287; r3    const uint8_t *limit,
288; sp    const uint8_t *thresh,
289; sp+4  int count
290|vp9_lpf_horizontal_8_neon| PROC
291    push        {r4-r5, lr}
292
293    vld1.8      {d0[]}, [r2]               ; duplicate *blimit
294    ldr         r12, [sp, #16]             ; load count
295    ldr         r2, [sp, #12]              ; load thresh
296    add         r1, r1, r1                 ; double pitch
297
298    cmp         r12, #0
299    beq         end_vp9_mblf_h_edge
300
301    vld1.8      {d1[]}, [r3]               ; duplicate *limit
302    vld1.8      {d2[]}, [r2]               ; duplicate *thresh
303
304count_mblf_h_loop
305    sub         r3, r0, r1, lsl #1         ; move src pointer down by 4 lines
306    add         r2, r3, r1, lsr #1         ; set to 3 lines down
307
308    vld1.u8     {d3}, [r3@64], r1          ; p3
309    vld1.u8     {d4}, [r2@64], r1          ; p2
310    vld1.u8     {d5}, [r3@64], r1          ; p1
311    vld1.u8     {d6}, [r2@64], r1          ; p0
312    vld1.u8     {d7}, [r3@64], r1          ; q0
313    vld1.u8     {d16}, [r2@64], r1         ; q1
314    vld1.u8     {d17}, [r3@64]             ; q2
315    vld1.u8     {d18}, [r2@64], r1         ; q3
316
317    sub         r3, r3, r1, lsl #1
318    sub         r2, r2, r1, lsl #2
319
320    bl          vp9_mbloop_filter_neon
321
322    vst1.u8     {d0}, [r2@64], r1          ; store op2
323    vst1.u8     {d1}, [r3@64], r1          ; store op1
324    vst1.u8     {d2}, [r2@64], r1          ; store op0
325    vst1.u8     {d3}, [r3@64], r1          ; store oq0
326    vst1.u8     {d4}, [r2@64], r1          ; store oq1
327    vst1.u8     {d5}, [r3@64], r1          ; store oq2
328
329    add         r0, r0, #8
330    subs        r12, r12, #1
331    bne         count_mblf_h_loop
332
333end_vp9_mblf_h_edge
334    pop         {r4-r5, pc}
335
336    ENDP        ; |vp9_lpf_horizontal_8_neon|
337
338; void vp9_lpf_vertical_8_neon(uint8_t *s,
339;                              int pitch,
340;                              const uint8_t *blimit,
341;                              const uint8_t *limit,
342;                              const uint8_t *thresh,
343;                              int count)
344;
345; r0    uint8_t *s,
346; r1    int pitch,
347; r2    const uint8_t *blimit,
348; r3    const uint8_t *limit,
349; sp    const uint8_t *thresh,
350; sp+4  int count
351|vp9_lpf_vertical_8_neon| PROC
352    push        {r4-r5, lr}
353
354    vld1.8      {d0[]}, [r2]              ; duplicate *blimit
355    ldr         r12, [sp, #16]            ; load count
356    vld1.8      {d1[]}, [r3]              ; duplicate *limit
357
358    ldr         r3, [sp, #12]             ; load thresh
359    sub         r2, r0, #4                ; move s pointer down by 4 columns
360    cmp         r12, #0
361    beq         end_vp9_mblf_v_edge
362
363    vld1.8      {d2[]}, [r3]              ; duplicate *thresh
364
365count_mblf_v_loop
366    vld1.u8     {d3}, [r2], r1             ; load s data
367    vld1.u8     {d4}, [r2], r1
368    vld1.u8     {d5}, [r2], r1
369    vld1.u8     {d6}, [r2], r1
370    vld1.u8     {d7}, [r2], r1
371    vld1.u8     {d16}, [r2], r1
372    vld1.u8     {d17}, [r2], r1
373    vld1.u8     {d18}, [r2]
374
375    ;transpose to 8x16 matrix
376    vtrn.32     d3, d7
377    vtrn.32     d4, d16
378    vtrn.32     d5, d17
379    vtrn.32     d6, d18
380
381    vtrn.16     d3, d5
382    vtrn.16     d4, d6
383    vtrn.16     d7, d17
384    vtrn.16     d16, d18
385
386    vtrn.8      d3, d4
387    vtrn.8      d5, d6
388    vtrn.8      d7, d16
389    vtrn.8      d17, d18
390
391    sub         r2, r0, #3
392    add         r3, r0, #1
393
394    bl          vp9_mbloop_filter_neon
395
396    ;store op2, op1, op0, oq0
397    vst4.8      {d0[0], d1[0], d2[0], d3[0]}, [r2], r1
398    vst4.8      {d0[1], d1[1], d2[1], d3[1]}, [r2], r1
399    vst4.8      {d0[2], d1[2], d2[2], d3[2]}, [r2], r1
400    vst4.8      {d0[3], d1[3], d2[3], d3[3]}, [r2], r1
401    vst4.8      {d0[4], d1[4], d2[4], d3[4]}, [r2], r1
402    vst4.8      {d0[5], d1[5], d2[5], d3[5]}, [r2], r1
403    vst4.8      {d0[6], d1[6], d2[6], d3[6]}, [r2], r1
404    vst4.8      {d0[7], d1[7], d2[7], d3[7]}, [r2]
405
406    ;store oq1, oq2
407    vst2.8      {d4[0], d5[0]}, [r3], r1
408    vst2.8      {d4[1], d5[1]}, [r3], r1
409    vst2.8      {d4[2], d5[2]}, [r3], r1
410    vst2.8      {d4[3], d5[3]}, [r3], r1
411    vst2.8      {d4[4], d5[4]}, [r3], r1
412    vst2.8      {d4[5], d5[5]}, [r3], r1
413    vst2.8      {d4[6], d5[6]}, [r3], r1
414    vst2.8      {d4[7], d5[7]}, [r3]
415
416    add         r0, r0, r1, lsl #3         ; s += pitch * 8
417    subs        r12, r12, #1
418    subne       r2, r0, #4                 ; move s pointer down by 4 columns
419    bne         count_mblf_v_loop
420
421end_vp9_mblf_v_edge
422    pop         {r4-r5, pc}
423    ENDP        ; |vp9_lpf_vertical_8_neon|
424
425; void vp9_mbloop_filter_neon();
426; This is a helper function for the loopfilters. The invidual functions do the
427; necessary load, transpose (if necessary) and store. The function does not use
428; registers d8-d15.
429;
430; Inputs:
431; r0-r3, r12 PRESERVE
432; d0    blimit
433; d1    limit
434; d2    thresh
435; d3    p3
436; d4    p2
437; d5    p1
438; d6    p0
439; d7    q0
440; d16   q1
441; d17   q2
442; d18   q3
443;
444; Outputs:
445; d0    op2
446; d1    op1
447; d2    op0
448; d3    oq0
449; d4    oq1
450; d5    oq2
451|vp9_mbloop_filter_neon| PROC
452    ; filter_mask
453    vabd.u8     d19, d3, d4                ; m1 = abs(p3 - p2)
454    vabd.u8     d20, d4, d5                ; m2 = abs(p2 - p1)
455    vabd.u8     d21, d5, d6                ; m3 = abs(p1 - p0)
456    vabd.u8     d22, d16, d7               ; m4 = abs(q1 - q0)
457    vabd.u8     d23, d17, d16              ; m5 = abs(q2 - q1)
458    vabd.u8     d24, d18, d17              ; m6 = abs(q3 - q2)
459
460    ; only compare the largest value to limit
461    vmax.u8     d19, d19, d20              ; m1 = max(m1, m2)
462    vmax.u8     d20, d21, d22              ; m2 = max(m3, m4)
463
464    vabd.u8     d25, d6, d4                ; m7 = abs(p0 - p2)
465
466    vmax.u8     d23, d23, d24              ; m3 = max(m5, m6)
467
468    vabd.u8     d26, d7, d17               ; m8 = abs(q0 - q2)
469
470    vmax.u8     d19, d19, d20
471
472    vabd.u8     d24, d6, d7                ; m9 = abs(p0 - q0)
473    vabd.u8     d27, d3, d6                ; m10 = abs(p3 - p0)
474    vabd.u8     d28, d18, d7               ; m11 = abs(q3 - q0)
475
476    vmax.u8     d19, d19, d23
477
478    vabd.u8     d23, d5, d16               ; a = abs(p1 - q1)
479    vqadd.u8    d24, d24, d24              ; b = abs(p0 - q0) * 2
480
481    ; abs () > limit
482    vcge.u8     d19, d1, d19
483
484    ; only compare the largest value to thresh
485    vmax.u8     d25, d25, d26              ; m4 = max(m7, m8)
486    vmax.u8     d26, d27, d28              ; m5 = max(m10, m11)
487
488    vshr.u8     d23, d23, #1               ; a = a / 2
489
490    vmax.u8     d25, d25, d26              ; m4 = max(m4, m5)
491
492    vqadd.u8    d24, d24, d23              ; a = b + a
493
494    vmax.u8     d20, d20, d25              ; m2 = max(m2, m4)
495
496    vmov.u8     d23, #1
497    vcge.u8     d24, d0, d24               ; a > blimit
498
499    vcgt.u8     d21, d21, d2               ; (abs(p1 - p0) > thresh)*-1
500
501    vcge.u8     d20, d23, d20              ; flat
502
503    vand        d19, d19, d24              ; mask
504
505    vcgt.u8     d23, d22, d2               ; (abs(q1 - q0) > thresh)*-1
506
507    vand        d20, d20, d19              ; flat & mask
508
509    vmov.u8     d22, #0x80
510
511    vorr        d23, d21, d23              ; hev
512
513    ; This instruction will truncate the "flat & mask" masks down to 4 bits
514    ; each to fit into one 32 bit arm register. The values are stored in
515    ; q10.64[0].
516    vshrn.u16   d30, q10, #4
517    vmov.u32    r4, d30[0]                 ; flat & mask 4bits
518
519    adds        r5, r4, #1                 ; Check for all 1's
520
521    ; If mask and flat are 1's for all vectors, then we only need to execute
522    ; the power branch for all vectors.
523    beq         power_branch_only
524
525    cmp         r4, #0                     ; Check for 0, set flag for later
526
527    ; mbfilter() function
528    ; filter() function
529    ; convert to signed
530    veor        d21, d7, d22               ; qs0
531    veor        d24, d6, d22               ; ps0
532    veor        d25, d5, d22               ; ps1
533    veor        d26, d16, d22              ; qs1
534
535    vmov.u8     d27, #3
536
537    vsub.s8     d28, d21, d24              ; ( qs0 - ps0)
538
539    vqsub.s8    d29, d25, d26              ; filter = clamp(ps1-qs1)
540
541    vmull.s8    q15, d28, d27              ; 3 * ( qs0 - ps0)
542
543    vand        d29, d29, d23              ; filter &= hev
544
545    vaddw.s8    q15, q15, d29              ; filter + 3 * (qs0 - ps0)
546
547    vmov.u8     d29, #4
548
549    ; filter = clamp(filter + 3 * ( qs0 - ps0))
550    vqmovn.s16  d28, q15
551
552    vand        d28, d28, d19              ; filter &= mask
553
554    vqadd.s8    d30, d28, d27              ; filter2 = clamp(filter+3)
555    vqadd.s8    d29, d28, d29              ; filter1 = clamp(filter+4)
556    vshr.s8     d30, d30, #3               ; filter2 >>= 3
557    vshr.s8     d29, d29, #3               ; filter1 >>= 3
558
559    vqadd.s8    d24, d24, d30              ; op0 = clamp(ps0 + filter2)
560    vqsub.s8    d21, d21, d29              ; oq0 = clamp(qs0 - filter1)
561
562    ; outer tap adjustments: ++filter1 >> 1
563    vrshr.s8    d29, d29, #1
564    vbic        d29, d29, d23              ; filter &= ~hev
565
566    vqadd.s8    d25, d25, d29              ; op1 = clamp(ps1 + filter)
567    vqsub.s8    d26, d26, d29              ; oq1 = clamp(qs1 - filter)
568
569    ; If mask and flat are 0's for all vectors, then we only need to execute
570    ; the filter branch for all vectors.
571    beq         filter_branch_only
572
573    ; If mask and flat are mixed then we must perform both branches and
574    ; combine the data.
575    veor        d24, d24, d22              ; *f_op0 = u^0x80
576    veor        d21, d21, d22              ; *f_oq0 = u^0x80
577    veor        d25, d25, d22              ; *f_op1 = u^0x80
578    veor        d26, d26, d22              ; *f_oq1 = u^0x80
579
580    ; At this point we have already executed the filter branch. The filter
581    ; branch does not set op2 or oq2, so use p2 and q2. Execute the power
582    ; branch and combine the data.
583    vmov.u8     d23, #2
584    vaddl.u8    q14, d6, d7                ; r_op2 = p0 + q0
585    vmlal.u8    q14, d3, d27               ; r_op2 += p3 * 3
586    vmlal.u8    q14, d4, d23               ; r_op2 += p2 * 2
587
588    vbif        d0, d4, d20                ; op2 |= p2 & ~(flat & mask)
589
590    vaddw.u8    q14, d5                    ; r_op2 += p1
591
592    vbif        d1, d25, d20               ; op1 |= f_op1 & ~(flat & mask)
593
594    vqrshrn.u16 d30, q14, #3               ; r_op2
595
596    vsubw.u8    q14, d3                    ; r_op1 = r_op2 - p3
597    vsubw.u8    q14, d4                    ; r_op1 -= p2
598    vaddw.u8    q14, d5                    ; r_op1 += p1
599    vaddw.u8    q14, d16                   ; r_op1 += q1
600
601    vbif        d2, d24, d20               ; op0 |= f_op0 & ~(flat & mask)
602
603    vqrshrn.u16 d31, q14, #3               ; r_op1
604
605    vsubw.u8    q14, d3                    ; r_op0 = r_op1 - p3
606    vsubw.u8    q14, d5                    ; r_op0 -= p1
607    vaddw.u8    q14, d6                    ; r_op0 += p0
608    vaddw.u8    q14, d17                   ; r_op0 += q2
609
610    vbit        d0, d30, d20               ; op2 |= r_op2 & (flat & mask)
611
612    vqrshrn.u16 d23, q14, #3               ; r_op0
613
614    vsubw.u8    q14, d3                    ; r_oq0 = r_op0 - p3
615    vsubw.u8    q14, d6                    ; r_oq0 -= p0
616    vaddw.u8    q14, d7                    ; r_oq0 += q0
617
618    vbit        d1, d31, d20               ; op1 |= r_op1 & (flat & mask)
619
620    vaddw.u8    q14, d18                   ; oq0 += q3
621
622    vbit        d2, d23, d20               ; op0 |= r_op0 & (flat & mask)
623
624    vqrshrn.u16 d22, q14, #3               ; r_oq0
625
626    vsubw.u8    q14, d4                    ; r_oq1 = r_oq0 - p2
627    vsubw.u8    q14, d7                    ; r_oq1 -= q0
628    vaddw.u8    q14, d16                   ; r_oq1 += q1
629
630    vbif        d3, d21, d20               ; oq0 |= f_oq0 & ~(flat & mask)
631
632    vaddw.u8    q14, d18                   ; r_oq1 += q3
633
634    vbif        d4, d26, d20               ; oq1 |= f_oq1 & ~(flat & mask)
635
636    vqrshrn.u16 d6, q14, #3                ; r_oq1
637
638    vsubw.u8    q14, d5                    ; r_oq2 = r_oq1 - p1
639    vsubw.u8    q14, d16                   ; r_oq2 -= q1
640    vaddw.u8    q14, d17                   ; r_oq2 += q2
641    vaddw.u8    q14, d18                   ; r_oq2 += q3
642
643    vbif        d5, d17, d20               ; oq2 |= q2 & ~(flat & mask)
644
645    vqrshrn.u16 d7, q14, #3                ; r_oq2
646
647    vbit        d3, d22, d20               ; oq0 |= r_oq0 & (flat & mask)
648    vbit        d4, d6, d20                ; oq1 |= r_oq1 & (flat & mask)
649    vbit        d5, d7, d20                ; oq2 |= r_oq2 & (flat & mask)
650
651    bx          lr
652
653power_branch_only
654    vmov.u8     d27, #3
655    vmov.u8     d21, #2
656    vaddl.u8    q14, d6, d7                ; op2 = p0 + q0
657    vmlal.u8    q14, d3, d27               ; op2 += p3 * 3
658    vmlal.u8    q14, d4, d21               ; op2 += p2 * 2
659    vaddw.u8    q14, d5                    ; op2 += p1
660    vqrshrn.u16 d0, q14, #3                ; op2
661
662    vsubw.u8    q14, d3                    ; op1 = op2 - p3
663    vsubw.u8    q14, d4                    ; op1 -= p2
664    vaddw.u8    q14, d5                    ; op1 += p1
665    vaddw.u8    q14, d16                   ; op1 += q1
666    vqrshrn.u16 d1, q14, #3                ; op1
667
668    vsubw.u8    q14, d3                    ; op0 = op1 - p3
669    vsubw.u8    q14, d5                    ; op0 -= p1
670    vaddw.u8    q14, d6                    ; op0 += p0
671    vaddw.u8    q14, d17                   ; op0 += q2
672    vqrshrn.u16 d2, q14, #3                ; op0
673
674    vsubw.u8    q14, d3                    ; oq0 = op0 - p3
675    vsubw.u8    q14, d6                    ; oq0 -= p0
676    vaddw.u8    q14, d7                    ; oq0 += q0
677    vaddw.u8    q14, d18                   ; oq0 += q3
678    vqrshrn.u16 d3, q14, #3                ; oq0
679
680    vsubw.u8    q14, d4                    ; oq1 = oq0 - p2
681    vsubw.u8    q14, d7                    ; oq1 -= q0
682    vaddw.u8    q14, d16                   ; oq1 += q1
683    vaddw.u8    q14, d18                   ; oq1 += q3
684    vqrshrn.u16 d4, q14, #3                ; oq1
685
686    vsubw.u8    q14, d5                    ; oq2 = oq1 - p1
687    vsubw.u8    q14, d16                   ; oq2 -= q1
688    vaddw.u8    q14, d17                   ; oq2 += q2
689    vaddw.u8    q14, d18                   ; oq2 += q3
690    vqrshrn.u16 d5, q14, #3                ; oq2
691
692    bx          lr
693
694filter_branch_only
695    ; TODO(fgalligan): See if we can rearange registers so we do not need to
696    ; do the 2 vswp.
697    vswp        d0, d4                      ; op2
698    vswp        d5, d17                     ; oq2
699    veor        d2, d24, d22                ; *op0 = u^0x80
700    veor        d3, d21, d22                ; *oq0 = u^0x80
701    veor        d1, d25, d22                ; *op1 = u^0x80
702    veor        d4, d26, d22                ; *oq1 = u^0x80
703
704    bx          lr
705
706    ENDP        ; |vp9_mbloop_filter_neon|
707
708    END
709