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