ad80f900dd17f6fa14ad7a8aea28359c20fabbd4
[theora.git] / lib / arm / armloop.s
1 ;********************************************************************
2 ;*                                                                  *
3 ;* THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.   *
4 ;* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
5 ;* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 ;* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
7 ;*                                                                  *
8 ;* THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2010                *
9 ;* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
10 ;*                                                                  *
11 ;********************************************************************
12 ; Original implementation:
13 ;  Copyright (C) 2009 Robin Watts for Pinknoise Productions Ltd
14 ; last mod: $Id$
15 ;********************************************************************
16
17         AREA    |.text|, CODE, READONLY
18
19         GET     armopts.s
20
21         EXPORT  oc_loop_filter_frag_rows_arm
22
23 ; Which bit this is depends on the order of packing within a bitfield.
24 ; Hopefully that doesn't change among any of the relevant compilers.
25 OC_FRAG_CODED_FLAG      *       1
26
27         ; Vanilla ARM v4 version
28 loop_filter_h_arm
29         ; r0 = unsigned char *_pix
30         ; r1 = int            _ystride
31         ; r2 = int           *_bv
32         ; preserves r0-r3
33         STMFD   r13!,{r3-r6,r14}
34         MOV     r14,#8
35         MOV     r6, #255
36 lfh_arm_lp
37         LDRB    r3, [r0, #-2]           ; r3 = _pix[0]
38         LDRB    r12,[r0, #1]            ; r12= _pix[3]
39         LDRB    r4, [r0, #-1]           ; r4 = _pix[1]
40         LDRB    r5, [r0]                ; r5 = _pix[2]
41         SUB     r3, r3, r12             ; r3 = _pix[0]-_pix[3]+4
42         ADD     r3, r3, #4
43         SUB     r12,r5, r4              ; r12= _pix[2]-_pix[1]
44         ADD     r12,r12,r12,LSL #1      ; r12= 3*(_pix[2]-_pix[1])
45         ADD     r12,r12,r3      ; r12= _pix[0]-_pix[3]+3*(_pix[2]-_pix[1])+4
46         MOV     r12,r12,ASR #3
47         LDRSB   r12,[r2, r12]
48         ; Stall (2 on Xscale)
49         ADDS    r4, r4, r12
50         CMPGT   r6, r4
51         EORLT   r4, r6, r4, ASR #32
52         SUBS    r5, r5, r12
53         CMPGT   r6, r5
54         EORLT   r5, r6, r5, ASR #32
55         STRB    r4, [r0, #-1]
56         STRB    r5, [r0], r1
57         SUBS    r14,r14,#1
58         BGT     lfh_arm_lp
59         SUB     r0, r0, r1, LSL #3
60         LDMFD   r13!,{r3-r6,PC}
61
62 loop_filter_v_arm
63         ; r0 = unsigned char *_pix
64         ; r1 = int            _ystride
65         ; r2 = int           *_bv
66         ; preserves r0-r3
67         STMFD   r13!,{r3-r6,r14}
68         MOV     r14,#8
69         MOV     r6, #255
70 lfv_arm_lp
71         LDRB    r3, [r0, -r1, LSL #1]   ; r3 = _pix[0]
72         LDRB    r12,[r0, r1]            ; r12= _pix[3]
73         LDRB    r4, [r0, -r1]           ; r4 = _pix[1]
74         LDRB    r5, [r0]                ; r5 = _pix[2]
75         SUB     r3, r3, r12             ; r3 = _pix[0]-_pix[3]+4
76         ADD     r3, r3, #4
77         SUB     r12,r5, r4              ; r12= _pix[2]-_pix[1]
78         ADD     r12,r12,r12,LSL #1      ; r12= 3*(_pix[2]-_pix[1])
79         ADD     r12,r12,r3      ; r12= _pix[0]-_pix[3]+3*(_pix[2]-_pix[1])+4
80         MOV     r12,r12,ASR #3
81         LDRSB   r12,[r2, r12]
82         ; Stall (2 on Xscale)
83         ADDS    r4, r4, r12
84         CMPGT   r6, r4
85         EORLT   r4, r6, r4, ASR #32
86         SUBS    r5, r5, r12
87         CMPGT   r6, r5
88         EORLT   r5, r6, r5, ASR #32
89         STRB    r4, [r0, -r1]
90         STRB    r5, [r0], #1
91         SUBS    r14,r14,#1
92         BGT     lfv_arm_lp
93         SUB     r0, r0, #8
94         LDMFD   r13!,{r3-r6,PC}
95
96 oc_loop_filter_frag_rows_arm
97         ; r0 = _ref_frame_data
98         ; r1 = _ystride
99         ; r2 = _bv
100         ; r3 = _frags
101         ; r4 = _fragi0
102         ; r5 = _fragi0_end
103         ; r6 = _fragi_top
104         ; r7 = _fragi_bot
105         ; r8 = _frag_buf_offs
106         ; r9 = _nhfrags
107         MOV     r12,r13
108         STMFD   r13!,{r0,r4-r11,r14}
109         LDMFD   r12,{r4-r9}
110         ADD     r2, r2, #127    ; _bv += 127
111         CMP     r4, r5          ; if(_fragi0>=_fragi0_end)
112         BGE     oslffri_arm_end ;   bail
113         SUBS    r9, r9, #1      ; r9 = _nhfrags-1       if (r9<=0)
114         BLE     oslffri_arm_end ;                         bail
115         ADD     r3, r3, r4, LSL #2      ; r3 = &_frags[fragi]
116         ADD     r8, r8, r4, LSL #2      ; r8 = &_frag_buf_offs[fragi]
117         SUB     r7, r7, r9      ; _fragi_bot -= _nhfrags;
118 oslffri_arm_lp1
119         MOV     r10,r4          ; r10= fragi = _fragi0
120         ADD     r11,r4, r9      ; r11= fragi_end-1=fragi+_nhfrags-1
121 oslffri_arm_lp2
122         LDR     r14,[r3], #4    ; r14= _frags[fragi]    _frags++
123         LDR     r0, [r13]       ; r0 = _ref_frame_data
124         LDR     r12,[r8], #4    ; r12= _frag_buf_offs[fragi]   _frag_buf_offs++
125         TST     r14,#OC_FRAG_CODED_FLAG
126         BEQ     oslffri_arm_uncoded
127         CMP     r10,r4          ; if (fragi>_fragi0)
128         ADD     r0, r0, r12     ; r0 = _ref_frame_data + _frag_buf_offs[fragi]
129         BLGT    loop_filter_h_arm
130         CMP     r4, r6          ; if (_fragi0>_fragi_top)
131         BLGT    loop_filter_v_arm
132         CMP     r10,r11         ; if(fragi+1<fragi_end)===(fragi<fragi_end-1)
133         LDRLT   r12,[r3]        ; r12 = _frags[fragi+1]
134         ADD     r0, r0, #8
135         ADD     r10,r10,#1      ; r10 = fragi+1;
136         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
137         CMPLT   r12,#OC_FRAG_CODED_FLAG ; && _frags[fragi+1].coded==0
138         BLLT    loop_filter_h_arm
139         CMP     r10,r7          ; if (fragi<_fragi_bot)
140         LDRLT   r12,[r3, r9, LSL #2]    ; r12 = _frags[fragi+1+_nhfrags-1]
141         SUB     r0, r0, #8
142         ADD     r0, r0, r1, LSL #3
143         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
144         CMPLT   r12,#OC_FRAG_CODED_FLAG
145         BLLT    loop_filter_v_arm
146         CMP     r10,r11         ; while(fragi<=fragi_end-1)
147         BLE     oslffri_arm_lp2
148         MOV     r4, r10         ; r4 = fragi0 += _nhfrags
149         CMP     r4, r5
150         BLT     oslffri_arm_lp1
151 oslffri_arm_end
152         LDMFD   r13!,{r0,r4-r11,PC}
153 oslffri_arm_uncoded
154         ADD     r10,r10,#1
155         CMP     r10,r11
156         BLE     oslffri_arm_lp2
157         MOV     r4, r10         ; r4 = _fragi0 += _nhfrags
158         CMP     r4, r5
159         BLT     oslffri_arm_lp1
160         LDMFD   r13!,{r0,r4-r11,PC}
161
162  [ OC_ARM_ASM_MEDIA
163         EXPORT  oc_loop_filter_init_v6
164         EXPORT  oc_loop_filter_frag_rows_v6
165
166 oc_loop_filter_init_v6
167         ; r0 = _bv
168         ; r1 = _flimit (=L from the spec)
169         MVN     r1, r1, LSL #1          ; r1 = <0xFFFFFF|255-2*L>
170         AND     r1, r1, #255            ; r1 = ll=r1&0xFF
171         ORR     r1, r1, r1, LSL #8      ; r1 = <ll|ll>
172         PKHBT   r1, r1, r1, LSL #16     ; r1 = <ll|ll|ll|ll>
173         STR     r1, [r0]
174         MOV     PC,r14
175
176 ; We could use the same strategy as the v filter below, but that would require
177 ;  40 instructions to load the data and transpose it into columns and another
178 ;  32 to write out the results at the end, plus the 52 instructions to do the
179 ;  filtering itself.
180 ; This is slightly less, and less code, even assuming we could have shared the
181 ;  52 instructions in the middle with the other function.
182 ; It executes slightly fewer instructions than the ARMv6 approach David Conrad
183 ;  proposed for FFmpeg, but not by much:
184 ;  http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2010-February/083141.html
185 ; His is a lot less code, though, because it only does two rows at once instead
186 ;  of four.
187 loop_filter_h_v6
188         ; r0 = unsigned char *_pix
189         ; r1 = int            _ystride
190         ; r2 = int            _ll
191         ; preserves r0-r3
192         STMFD   r13!,{r4-r11,r14}
193         LDR     r12,=0x10003
194         BL loop_filter_h_core_v6
195         ADD     r0, r0, r1, LSL #2
196         BL loop_filter_h_core_v6
197         SUB     r0, r0, r1, LSL #2
198         LDMFD   r13!,{r4-r11,PC}
199
200 loop_filter_h_core_v6
201         ; r0 = unsigned char *_pix
202         ; r1 = int            _ystride
203         ; r2 = int            _ll
204         ; r12= 0x10003
205         ; Preserves r0-r3, r12; Clobbers r4-r11.
206         LDR     r4,[r0, #-2]!           ; r4 = <p3|p2|p1|p0>
207         ; Single issue
208         LDR     r5,[r0, r1]!            ; r5 = <q3|q2|q1|q0>
209         UXTB16  r6, r4, ROR #16         ; r6 = <p0|p2>
210         UXTB16  r4, r4, ROR #8          ; r4 = <p3|p1>
211         UXTB16  r7, r5, ROR #16         ; r7 = <q0|q2>
212         UXTB16  r5, r5, ROR #8          ; r5 = <q3|q1>
213         PKHBT   r8, r4, r5, LSL #16     ; r8 = <__|q1|__|p1>
214         PKHBT   r9, r6, r7, LSL #16     ; r9 = <__|q2|__|p2>
215         SSUB16  r6, r4, r6              ; r6 = <p3-p0|p1-p2>
216         SMLAD   r6, r6, r12,r12         ; r6 = <????|(p3-p0)+3*(p1-p2)+3>
217         SSUB16  r7, r5, r7              ; r7 = <q3-q0|q1-q2>
218         SMLAD   r7, r7, r12,r12         ; r7 = <????|(q0-q3)+3*(q2-q1)+4>
219         LDR     r4,[r0, r1]!            ; r4 = <r3|r2|r1|r0>
220         MOV     r6, r6, ASR #3          ; r6 = <??????|(p3-p0)+3*(p1-p2)+3>>3>
221         LDR     r5,[r0, r1]!            ; r5 = <s3|s2|s1|s0>
222         PKHBT   r11,r6, r7, LSL #13     ; r11= <??|-R_q|??|-R_p>
223         UXTB16  r6, r4, ROR #16         ; r6 = <r0|r2>
224         UXTB16  r11,r11                 ; r11= <__|-R_q|__|-R_p>
225         UXTB16  r4, r4, ROR #8          ; r4 = <r3|r1>
226         UXTB16  r7, r5, ROR #16         ; r7 = <s0|s2>
227         PKHBT   r10,r6, r7, LSL #16     ; r10= <__|s2|__|r2>
228         SSUB16  r6, r4, r6              ; r6 = <r3-r0|r1-r2>
229         UXTB16  r5, r5, ROR #8          ; r5 = <s3|s1>
230         SMLAD   r6, r6, r12,r12         ; r6 = <????|(r3-r0)+3*(r2-r1)+3>
231         SSUB16  r7, r5, r7              ; r7 = <r3-r0|r1-r2>
232         SMLAD   r7, r7, r12,r12         ; r7 = <????|(s0-s3)+3*(s2-s1)+4>
233         ORR     r9, r9, r10, LSL #8     ; r9 = <s2|q2|r2|p2>
234         MOV     r6, r6, ASR #3          ; r6 = <??????|(r0-r3)+3*(r2-r1)+4>>3>
235         PKHBT   r10,r4, r5, LSL #16     ; r10= <__|s1|__|r1>
236         PKHBT   r6, r6, r7, LSL #13     ; r6 = <??|-R_s|??|-R_r>
237         ORR     r8, r8, r10, LSL #8     ; r8 = <s1|q1|r1|p1>
238         UXTB16  r6, r6                  ; r6 = <__|-R_s|__|-R_r>
239         MOV     r10,#0
240         ORR     r6, r11,r6, LSL #8      ; r6 = <-R_s|-R_q|-R_r|-R_p>
241         ; Single issue
242         ; There's no min, max or abs instruction.
243         ; SSUB8 and SEL will work for abs, and we can do all the rest with
244         ;  unsigned saturated adds, which means the GE flags are still all
245         ;  set when we're done computing lflim(abs(R_i),L).
246         ; This allows us to both add and subtract, and split the results by
247         ;  the original sign of R_i.
248         SSUB8   r7, r10,r6
249         ; Single issue
250         SEL     r7, r7, r6              ; r7 = abs(R_i)
251         ; Single issue
252         UQADD8  r4, r7, r2              ; r4 = 255-max(2*L-abs(R_i),0)
253         ; Single issue
254         UQADD8  r7, r7, r4
255         ; Single issue
256         UQSUB8  r7, r7, r4              ; r7 = min(abs(R_i),max(2*L-abs(R_i),0))
257         ; Single issue
258         UQSUB8  r4, r8, r7
259         UQADD8  r5, r9, r7
260         UQADD8  r8, r8, r7
261         UQSUB8  r9, r9, r7
262         SEL     r8, r8, r4              ; r8 = p1+lflim(R_i,L)
263         SEL     r9, r9, r5              ; r9 = p2-lflim(R_i,L)
264         MOV     r5, r9, LSR #24         ; r5 = s2
265         STRB    r5, [r0,#2]!
266         MOV     r4, r8, LSR #24         ; r4 = s1
267         STRB    r4, [r0,#-1]
268         MOV     r5, r9, LSR #8          ; r5 = r2
269         STRB    r5, [r0,-r1]!
270         MOV     r4, r8, LSR #8          ; r4 = r1
271         STRB    r4, [r0,#-1]
272         MOV     r5, r9, LSR #16         ; r5 = q2
273         STRB    r5, [r0,-r1]!
274         MOV     r4, r8, LSR #16         ; r4 = q1
275         STRB    r4, [r0,#-1]
276         ; Single issue
277         STRB    r9, [r0,-r1]!
278         ; Single issue
279         STRB    r8, [r0,#-1]
280         MOV     PC,r14
281
282 ; This uses the same strategy as the MMXEXT version for x86, except that UHADD8
283 ;  computes (a+b>>1) instead of (a+b+1>>1) like PAVGB.
284 ; This works just as well, with the following procedure for computing the
285 ;  filter value, f:
286 ;   u = ~UHADD8(p1,~p2);
287 ;   v = UHADD8(~p1,p2);
288 ;   m = v-u;
289 ;   a = m^UHADD8(m^p0,m^~p3);
290 ;   f = UHADD8(UHADD8(a,u1),v1);
291 ;  where f = 127+R, with R in [-127,128] defined as in the spec.
292 ; This is exactly the same amount of arithmetic as the version that uses PAVGB
293 ;  as the basic operator.
294 ; It executes about 2/3 the number of instructions of David Conrad's approach,
295 ;  but requires more code, because it does all eight columns at once, instead
296 ;  of four at a time.
297 loop_filter_v_v6
298         ; r0 = unsigned char *_pix
299         ; r1 = int            _ystride
300         ; r2 = int            _ll
301         ; preserves r0-r11
302         STMFD   r13!,{r4-r11,r14}
303         LDRD    r6, [r0, -r1]!          ; r7, r6 = <p5|p1>
304         LDRD    r4, [r0, -r1]           ; r5, r4 = <p4|p0>
305         LDRD    r8, [r0, r1]!           ; r9, r8 = <p6|p2>
306         MVN     r14,r6                  ; r14= ~p1
307         LDRD    r10,[r0, r1]            ; r11,r10= <p7|p3>
308         ; Filter the first four columns.
309         MVN     r12,r8                  ; r12= ~p2
310         UHADD8  r14,r14,r8              ; r14= v1=~p1+p2>>1
311         UHADD8  r12,r12,r6              ; r12= p1+~p2>>1
312         MVN     r10, r10                ; r10=~p3
313         MVN     r12,r12                 ; r12= u1=~p1+p2+1>>1
314         SSUB8   r14,r14,r12             ; r14= m1=v1-u1
315         ; Single issue
316         EOR     r4, r4, r14             ; r4 = m1^p0
317         EOR     r10,r10,r14             ; r10= m1^~p3
318         UHADD8  r4, r4, r10             ; r4 = (m1^p0)+(m1^~p3)>>1
319         ; Single issue
320         EOR     r4, r4, r14             ; r4 = a1=m1^((m1^p0)+(m1^~p3)>>1)
321         SADD8   r14,r14,r12             ; r14= v1=m1+u1
322         UHADD8  r4, r4, r12             ; r4 = a1+u1>>1
323         MVN     r12,r9                  ; r12= ~p6
324         UHADD8  r4, r4, r14             ; r4 = f1=(a1+u1>>1)+v1>>1
325         ; Filter the second four columns.
326         MVN     r14,r7                  ; r14= ~p5
327         UHADD8  r12,r12,r7              ; r12= p5+~p6>>1
328         UHADD8  r14,r14,r9              ; r14= v2=~p5+p6>>1
329         MVN     r12,r12                 ; r12= u2=~p5+p6+1>>1
330         MVN     r11,r11                 ; r11=~p7
331         SSUB8   r10,r14,r12             ; r10= m2=v2-u2
332         ; Single issue
333         EOR     r5, r5, r10             ; r5 = m2^p4
334         EOR     r11,r11,r10             ; r11= m2^~p7
335         UHADD8  r5, r5, r11             ; r5 = (m2^p4)+(m2^~p7)>>1
336         ; Single issue
337         EOR     r5, r5, r10             ; r5 = a2=m2^((m2^p4)+(m2^~p7)>>1)
338         ; Single issue
339         UHADD8  r5, r5, r12             ; r5 = a2+u2>>1
340         LDR     r12,=0x7F7F7F7F         ; r12 = {127}x4
341         UHADD8  r5, r5, r14             ; r5 = f2=(a2+u2>>1)+v2>>1
342         ; Now split f[i] by sign.
343         ; There's no min or max instruction.
344         ; We could use SSUB8 and SEL, but this is just as many instructions and
345         ;  dual issues more (for v7 without NEON).
346         UQSUB8  r10,r4, r12             ; r10= R_i>0?R_i:0
347         UQSUB8  r4, r12,r4              ; r4 = R_i<0?-R_i:0
348         UQADD8  r11,r10,r2              ; r11= 255-max(2*L-abs(R_i<0),0)
349         UQADD8  r14,r4, r2              ; r14= 255-max(2*L-abs(R_i>0),0)
350         UQADD8  r10,r10,r11
351         UQADD8  r4, r4, r14
352         UQSUB8  r10,r10,r11             ; r10= min(abs(R_i<0),max(2*L-abs(R_i<0),0))
353         UQSUB8  r4, r4, r14             ; r4 = min(abs(R_i>0),max(2*L-abs(R_i>0),0))
354         UQSUB8  r11,r5, r12             ; r11= R_i>0?R_i:0
355         UQADD8  r6, r6, r10
356         UQSUB8  r8, r8, r10
357         UQSUB8  r5, r12,r5              ; r5 = R_i<0?-R_i:0
358         UQSUB8  r6, r6, r4              ; r6 = p1+lflim(R_i,L)
359         UQADD8  r8, r8, r4              ; r8 = p2-lflim(R_i,L)
360         UQADD8  r10,r11,r2              ; r10= 255-max(2*L-abs(R_i<0),0)
361         UQADD8  r14,r5, r2              ; r14= 255-max(2*L-abs(R_i>0),0)
362         UQADD8  r11,r11,r10
363         UQADD8  r5, r5, r14
364         UQSUB8  r11,r11,r10             ; r11= min(abs(R_i<0),max(2*L-abs(R_i<0),0))
365         UQSUB8  r5, r5, r14             ; r5 = min(abs(R_i>0),max(2*L-abs(R_i>0),0))
366         UQADD8  r7, r7, r11
367         UQSUB8  r9, r9, r11
368         UQSUB8  r7, r7, r5              ; r7 = p5+lflim(R_i,L)
369         STRD    r6, [r0, -r1]           ; [p5:p1] = [r7: r6]
370         UQADD8  r9, r9, r5              ; r9 = p6-lflim(R_i,L)
371         STRD    r8, [r0]                ; [p6:p2] = [r9: r8]
372         LDMFD   r13!,{r4-r11,PC}
373
374 oc_loop_filter_frag_rows_v6
375         ; r0 = _ref_frame_data
376         ; r1 = _ystride
377         ; r2 = _bv
378         ; r3 = _frags
379         ; r4 = _fragi0
380         ; r5 = _fragi0_end
381         ; r6 = _fragi_top
382         ; r7 = _fragi_bot
383         ; r8 = _frag_buf_offs
384         ; r9 = _nhfrags
385         MOV     r12,r13
386         STMFD   r13!,{r0,r4-r11,r14}
387         LDMFD   r12,{r4-r9}
388         LDR     r2, [r2]        ; ll = *(int *)_bv
389         CMP     r4, r5          ; if(_fragi0>=_fragi0_end)
390         BGE     oslffri_v6_end  ;   bail
391         SUBS    r9, r9, #1      ; r9 = _nhfrags-1       if (r9<=0)
392         BLE     oslffri_v6_end  ;                         bail
393         ADD     r3, r3, r4, LSL #2      ; r3 = &_frags[fragi]
394         ADD     r8, r8, r4, LSL #2      ; r8 = &_frag_buf_offs[fragi]
395         SUB     r7, r7, r9      ; _fragi_bot -= _nhfrags;
396 oslffri_v6_lp1
397         MOV     r10,r4          ; r10= fragi = _fragi0
398         ADD     r11,r4, r9      ; r11= fragi_end-1=fragi+_nhfrags-1
399 oslffri_v6_lp2
400         LDR     r14,[r3], #4    ; r14= _frags[fragi]    _frags++
401         LDR     r0, [r13]       ; r0 = _ref_frame_data
402         LDR     r12,[r8], #4    ; r12= _frag_buf_offs[fragi]   _frag_buf_offs++
403         TST     r14,#OC_FRAG_CODED_FLAG
404         BEQ     oslffri_v6_uncoded
405         CMP     r10,r4          ; if (fragi>_fragi0)
406         ADD     r0, r0, r12     ; r0 = _ref_frame_data + _frag_buf_offs[fragi]
407         BLGT    loop_filter_h_v6
408         CMP     r4, r6          ; if (fragi0>_fragi_top)
409         BLGT    loop_filter_v_v6
410         CMP     r10,r11         ; if(fragi+1<fragi_end)===(fragi<fragi_end-1)
411         LDRLT   r12,[r3]        ; r12 = _frags[fragi+1]
412         ADD     r0, r0, #8
413         ADD     r10,r10,#1      ; r10 = fragi+1;
414         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
415         CMPLT   r12,#OC_FRAG_CODED_FLAG ; && _frags[fragi+1].coded==0
416         BLLT    loop_filter_h_v6
417         CMP     r10,r7          ; if (fragi<_fragi_bot)
418         LDRLT   r12,[r3, r9, LSL #2]    ; r12 = _frags[fragi+1+_nhfrags-1]
419         SUB     r0, r0, #8
420         ADD     r0, r0, r1, LSL #3
421         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
422         CMPLT   r12,#OC_FRAG_CODED_FLAG
423         BLLT    loop_filter_v_v6
424         CMP     r10,r11         ; while(fragi<=fragi_end-1)
425         BLE     oslffri_v6_lp2
426         MOV     r4, r10         ; r4 = fragi0 += nhfrags
427         CMP     r4, r5
428         BLT     oslffri_v6_lp1
429 oslffri_v6_end
430         LDMFD   r13!,{r0,r4-r11,PC}
431 oslffri_v6_uncoded
432         ADD     r10,r10,#1
433         CMP     r10,r11
434         BLE     oslffri_v6_lp2
435         MOV     r4, r10         ; r4 = fragi0 += nhfrags
436         CMP     r4, r5
437         BLT     oslffri_v6_lp1
438         LDMFD   r13!,{r0,r4-r11,PC}
439  ]
440
441  [ OC_ARM_ASM_NEON
442         EXPORT  oc_loop_filter_init_neon
443         EXPORT  oc_loop_filter_frag_rows_neon
444
445 oc_loop_filter_init_neon
446         ; r0 = _bv
447         ; r1 = _flimit (=L from the spec)
448         MOV             r1, r1, LSL #1  ; r1 = 2*L
449         VDUP.S16        Q15, r1         ; Q15= 2L in U16s
450         VST1.64         {D30,D31}, [r0@128]
451         MOV     PC,r14
452
453 loop_filter_h_neon
454         ; r0 = unsigned char *_pix
455         ; r1 = int            _ystride
456         ; r2 = int           *_bv
457         ; preserves r0-r3
458         ; We assume Q15= 2*L in U16s
459         ;                    My best guesses at cycle counts (and latency)--vvv
460         SUB     r12,r0, #2
461         ; Doing a 2-element structure load saves doing two VTRN's below, at the
462         ;  cost of using two more slower single-lane loads vs. the faster
463         ;  all-lane loads.
464         ; It's less code this way, though, and benches a hair faster, but it
465         ;  leaves D2 and D4 swapped.
466         VLD2.16 {D0[],D2[]},  [r12], r1         ; D0 = ____________1100     2,1
467                                                 ; D2 = ____________3322
468         VLD2.16 {D4[],D6[]},  [r12], r1         ; D4 = ____________5544     2,1
469                                                 ; D6 = ____________7766
470         VLD2.16 {D0[1],D2[1]},[r12], r1         ; D0 = ________99881100     3,1
471                                                 ; D2 = ________BBAA3322
472         VLD2.16 {D4[1],D6[1]},[r12], r1         ; D4 = ________DDCC5544     3,1
473                                                 ; D6 = ________FFEE7766
474         VLD2.16 {D0[2],D2[2]},[r12], r1         ; D0 = ____GGHH99881100     3,1
475                                                 ; D2 = ____JJIIBBAA3322
476         VLD2.16 {D4[2],D6[2]},[r12], r1         ; D4 = ____KKLLDDCC5544     3,1
477                                                 ; D6 = ____NNMMFFEE7766
478         VLD2.16 {D0[3],D2[3]},[r12], r1         ; D0 = PPOOGGHH99881100     3,1
479                                                 ; D2 = RRQQJJIIBBAA3322
480         VLD2.16 {D4[3],D6[3]},[r12], r1         ; D4 = TTSSKKLLDDCC5544     3,1
481                                                 ; D6 = VVUUNNMMFFEE7766
482         VTRN.8  D0, D4  ; D0 = SSOOKKGGCC884400 D4 = TTPPLLHHDD995511       1,1
483         VTRN.8  D2, D6  ; D2 = UUQQMMIIEEAA6622 D6 = VVRRNNJJFFBB7733       1,1
484         VSUBL.U8        Q0, D0, D6      ; Q0 = 00 - 33 in S16s              1,3
485         VSUBL.U8        Q8, D2, D4      ; Q8 = 22 - 11 in S16s              1,3
486         ADD     r12,r0, #8
487         VADD.S16        Q0, Q0, Q8      ;                                   1,3
488         PLD     [r12]
489         VADD.S16        Q0, Q0, Q8      ;                                   1,3
490         PLD     [r12,r1]
491         VADD.S16        Q0, Q0, Q8      ; Q0 = [0-3]+3*[2-1]                1,3
492         PLD     [r12,r1, LSL #1]
493         VRSHR.S16       Q0, Q0, #3      ; Q0 = f = ([0-3]+3*[2-1]+4)>>3     1,4
494         ADD     r12,r12,r1, LSL #2
495         ;  We want to do
496         ; f =             CLAMP(MIN(-2L-f,0), f, MAX(2L-f,0))
497         ;   = ((f >= 0) ? MIN( f ,MAX(2L- f ,0)) : MAX(  f , MIN(-2L- f ,0)))
498         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) : MAX(-|f|, MIN(-2L+|f|,0)))
499         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) :-MIN( |f|,-MIN(-2L+|f|,0)))
500         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) :-MIN( |f|, MAX( 2L-|f|,0)))
501         ; So we've reduced the left and right hand terms to be the same, except
502         ; for a negation.
503         ; Stall x3
504         VABS.S16        Q9, Q0          ; Q9 = |f| in U16s                  1,4
505         PLD     [r12,-r1]
506         VSHR.S16        Q0, Q0, #15     ; Q0 = -1 or 0 according to sign    1,3
507         PLD     [r12]
508         VQSUB.U16       Q10,Q15,Q9      ; Q10= MAX(2L-|f|,0) in U16s        1,4
509         PLD     [r12,r1]
510         VMOVL.U8        Q1, D2     ; Q2 = __UU__QQ__MM__II__EE__AA__66__22  2,3
511         PLD     [r12,r1,LSL #1]
512         VMIN.U16        Q9, Q10,Q9      ; Q9 = MIN(|f|,MAX(2L-|f|))         1,4
513         ADD     r12,r12,r1, LSL #2
514         ; Now we need to correct for the sign of f.
515         ; For negative elements of Q0, we want to subtract the appropriate
516         ; element of Q9. For positive elements we want to add them. No NEON
517         ; instruction exists to do this, so we need to negate the negative
518         ; elements, and we can then just add them. a-b = a-(1+!b) = a-1+!b
519         VADD.S16        Q9, Q9, Q0      ;                                   1,3
520         PLD     [r12,-r1]
521         VEOR.S16        Q9, Q9, Q0      ; Q9 = real value of f              1,3
522         ; Bah. No VRSBW.U8
523         ; Stall (just 1 as Q9 not needed to second pipeline stage. I think.)
524         VADDW.U8        Q2, Q9, D4 ; Q1 = xxTTxxPPxxLLxxHHxxDDxx99xx55xx11  1,3
525         VSUB.S16        Q1, Q1, Q9 ; Q2 = xxUUxxQQxxMMxxIIxxEExxAAxx66xx22  1,3
526         VQMOVUN.S16     D4, Q2          ; D4 = TTPPLLHHDD995511             1,1
527         VQMOVUN.S16     D2, Q1          ; D2 = UUQQMMIIEEAA6622             1,1
528         SUB     r12,r0, #1
529         VTRN.8  D4, D2          ; D4 = QQPPIIHHAA992211 D2 = MMLLEEDD6655   1,1
530         VST1.16 {D4[0]}, [r12], r1
531         VST1.16 {D2[0]}, [r12], r1
532         VST1.16 {D4[1]}, [r12], r1
533         VST1.16 {D2[1]}, [r12], r1
534         VST1.16 {D4[2]}, [r12], r1
535         VST1.16 {D2[2]}, [r12], r1
536         VST1.16 {D4[3]}, [r12], r1
537         VST1.16 {D2[3]}, [r12], r1
538         MOV     PC,r14
539
540 loop_filter_v_neon
541         ; r0 = unsigned char *_pix
542         ; r1 = int            _ystride
543         ; r2 = int           *_bv
544         ; preserves r0-r3
545         ; We assume Q15= 2*L in U16s
546         ;                    My best guesses at cycle counts (and latency)--vvv
547         SUB     r12,r0, r1, LSL #1
548         VLD1.64 {D0}, [r12@64], r1              ; D0 = SSOOKKGGCC884400     2,1
549         VLD1.64 {D2}, [r12@64], r1              ; D2 = TTPPLLHHDD995511     2,1
550         VLD1.64 {D4}, [r12@64], r1              ; D4 = UUQQMMIIEEAA6622     2,1
551         VLD1.64 {D6}, [r12@64]                  ; D6 = VVRRNNJJFFBB7733     2,1
552         VSUBL.U8        Q8, D4, D2      ; Q8 = 22 - 11 in S16s              1,3
553         VSUBL.U8        Q0, D0, D6      ; Q0 = 00 - 33 in S16s              1,3
554         ADD     r12, #8
555         VADD.S16        Q0, Q0, Q8      ;                                   1,3
556         PLD     [r12]
557         VADD.S16        Q0, Q0, Q8      ;                                   1,3
558         PLD     [r12,r1]
559         VADD.S16        Q0, Q0, Q8      ; Q0 = [0-3]+3*[2-1]                1,3
560         SUB     r12, r0, r1
561         VRSHR.S16       Q0, Q0, #3      ; Q0 = f = ([0-3]+3*[2-1]+4)>>3     1,4
562         ;  We want to do
563         ; f =             CLAMP(MIN(-2L-f,0), f, MAX(2L-f,0))
564         ;   = ((f >= 0) ? MIN( f ,MAX(2L- f ,0)) : MAX(  f , MIN(-2L- f ,0)))
565         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) : MAX(-|f|, MIN(-2L+|f|,0)))
566         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) :-MIN( |f|,-MIN(-2L+|f|,0)))
567         ;   = ((f >= 0) ? MIN(|f|,MAX(2L-|f|,0)) :-MIN( |f|, MAX( 2L-|f|,0)))
568         ; So we've reduced the left and right hand terms to be the same, except
569         ; for a negation.
570         ; Stall x3
571         VABS.S16        Q9, Q0          ; Q9 = |f| in U16s                  1,4
572         VSHR.S16        Q0, Q0, #15     ; Q0 = -1 or 0 according to sign    1,3
573         ; Stall x2
574         VQSUB.U16       Q10,Q15,Q9      ; Q10= MAX(2L-|f|,0) in U16s        1,4
575         VMOVL.U8        Q2, D4     ; Q2 = __UU__QQ__MM__II__EE__AA__66__22  2,3
576         ; Stall x2
577         VMIN.U16        Q9, Q10,Q9      ; Q9 = MIN(|f|,MAX(2L-|f|))         1,4
578         ; Now we need to correct for the sign of f.
579         ; For negative elements of Q0, we want to subtract the appropriate
580         ; element of Q9. For positive elements we want to add them. No NEON
581         ; instruction exists to do this, so we need to negate the negative
582         ; elements, and we can then just add them. a-b = a-(1+!b) = a-1+!b
583         ; Stall x3
584         VADD.S16        Q9, Q9, Q0      ;                                   1,3
585         ; Stall x2
586         VEOR.S16        Q9, Q9, Q0      ; Q9 = real value of f              1,3
587         ; Bah. No VRSBW.U8
588         ; Stall (just 1 as Q9 not needed to second pipeline stage. I think.)
589         VADDW.U8        Q1, Q9, D2 ; Q1 = xxTTxxPPxxLLxxHHxxDDxx99xx55xx11  1,3
590         VSUB.S16        Q2, Q2, Q9 ; Q2 = xxUUxxQQxxMMxxIIxxEExxAAxx66xx22  1,3
591         VQMOVUN.S16     D2, Q1          ; D2 = TTPPLLHHDD995511             1,1
592         VQMOVUN.S16     D4, Q2          ; D4 = UUQQMMIIEEAA6622             1,1
593         VST1.64 {D2}, [r12@64], r1
594         VST1.64 {D4}, [r12@64], r1
595         MOV     PC,r14
596
597 oc_loop_filter_frag_rows_neon
598         ; r0 = _ref_frame_data
599         ; r1 = _ystride
600         ; r2 = _bv
601         ; r3 = _frags
602         ; r4 = _fragi0
603         ; r5 = _fragi0_end
604         ; r6 = _fragi_top
605         ; r7 = _fragi_bot
606         ; r8 = _frag_buf_offs
607         ; r9 = _nhfrags
608         MOV     r12,r13
609         STMFD   r13!,{r0,r4-r11,r14}
610         LDMFD   r12,{r4-r9}
611         CMP     r4, r5          ; if(_fragi0>=_fragi0_end)
612         BGE     oslffri_neon_end;   bail
613         SUBS    r9, r9, #1      ; r9 = _nhfrags-1       if (r9<=0)
614         BLE     oslffri_neon_end        ;                 bail
615         VLD1.64 {D30,D31}, [r2@128]     ; Q15= 2L in U16s
616         ADD     r3, r3, r4, LSL #2      ; r3 = &_frags[fragi]
617         ADD     r8, r8, r4, LSL #2      ; r8 = &_frag_buf_offs[fragi]
618         SUB     r7, r7, r9      ; _fragi_bot -= _nhfrags;
619 oslffri_neon_lp1
620         MOV     r10,r4          ; r10= fragi = _fragi0
621         ADD     r11,r4, r9      ; r11= fragi_end-1=fragi+_nhfrags-1
622 oslffri_neon_lp2
623         LDR     r14,[r3], #4    ; r14= _frags[fragi]    _frags++
624         LDR     r0, [r13]       ; r0 = _ref_frame_data
625         LDR     r12,[r8], #4    ; r12= _frag_buf_offs[fragi]   _frag_buf_offs++
626         TST     r14,#OC_FRAG_CODED_FLAG
627         BEQ     oslffri_neon_uncoded
628         CMP     r10,r4          ; if (fragi>_fragi0)
629         ADD     r0, r0, r12     ; r0 = _ref_frame_data + _frag_buf_offs[fragi]
630         BLGT    loop_filter_h_neon
631         CMP     r4, r6          ; if (_fragi0>_fragi_top)
632         BLGT    loop_filter_v_neon
633         CMP     r10,r11         ; if(fragi+1<fragi_end)===(fragi<fragi_end-1)
634         LDRLT   r12,[r3]        ; r12 = _frags[fragi+1]
635         ADD     r0, r0, #8
636         ADD     r10,r10,#1      ; r10 = fragi+1;
637         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
638         CMPLT   r12,#OC_FRAG_CODED_FLAG ; && _frags[fragi+1].coded==0
639         BLLT    loop_filter_h_neon
640         CMP     r10,r7          ; if (fragi<_fragi_bot)
641         LDRLT   r12,[r3, r9, LSL #2]    ; r12 = _frags[fragi+1+_nhfrags-1]
642         SUB     r0, r0, #8
643         ADD     r0, r0, r1, LSL #3
644         ANDLT   r12,r12,#OC_FRAG_CODED_FLAG
645         CMPLT   r12,#OC_FRAG_CODED_FLAG
646         BLLT    loop_filter_v_neon
647         CMP     r10,r11         ; while(fragi<=fragi_end-1)
648         BLE     oslffri_neon_lp2
649         MOV     r4, r10         ; r4 = _fragi0 += _nhfrags
650         CMP     r4, r5
651         BLT     oslffri_neon_lp1
652 oslffri_neon_end
653         LDMFD   r13!,{r0,r4-r11,PC}
654 oslffri_neon_uncoded
655         ADD     r10,r10,#1
656         CMP     r10,r11
657         BLE     oslffri_neon_lp2
658         MOV     r4, r10         ; r4 = _fragi0 += _nhfrags
659         CMP     r4, r5
660         BLT     oslffri_neon_lp1
661         LDMFD   r13!,{r0,r4-r11,PC}
662  ]
663
664         END