1 /*
2  *  Copyright 2011 The LibYuv 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 #include "libyuv/basic_types.h"
12 #include "libyuv/row.h"
13 
14 #ifdef __cplusplus
15 namespace libyuv {
16 extern "C" {
17 #endif
18 
19 // This module is for GCC Neon
20 #if !defined(YUV_DISABLE_ASM) && defined(__ARM_NEON__)
21 
22 /**
23  * NEON downscalers with interpolation.
24  *
25  * Provided by Fritz Koenig
26  *
27  */
28 
ScaleRowDown2_NEON(const uint8 * src_ptr,ptrdiff_t,uint8 * dst,int dst_width)29 void ScaleRowDown2_NEON(const uint8* src_ptr, ptrdiff_t /* src_stride */,
30                         uint8* dst, int dst_width) {
31   asm volatile (
32     "1:                                        \n"
33     // load even pixels into q0, odd into q1
34     "vld2.u8    {q0,q1}, [%0]!                 \n"
35     "vst1.u8    {q0}, [%1]!                    \n"  // store even pixels
36     "subs       %2, %2, #16                    \n"  // 16 processed per loop
37     "bgt        1b                             \n"
38     : "+r"(src_ptr),          // %0
39       "+r"(dst),              // %1
40       "+r"(dst_width)         // %2
41     :
42     : "q0", "q1"              // Clobber List
43   );
44 }
45 
ScaleRowDown2Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst,int dst_width)46 void ScaleRowDown2Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
47                            uint8* dst, int dst_width) {
48   asm volatile (
49     // change the stride to row 2 pointer
50     "add        %1, %0                         \n"
51     "1:                                        \n"
52     "vld1.u8    {q0,q1}, [%0]!                 \n"  // load row 1 and post inc
53     "vld1.u8    {q2,q3}, [%1]!                 \n"  // load row 2 and post inc
54     "vpaddl.u8  q0, q0                         \n"  // row 1 add adjacent
55     "vpaddl.u8  q1, q1                         \n"
56     "vpadal.u8  q0, q2                         \n"  // row 2 add adjacent + row1
57     "vpadal.u8  q1, q3                         \n"
58     "vrshrn.u16 d0, q0, #2                     \n"  // downshift, round and pack
59     "vrshrn.u16 d1, q1, #2                     \n"
60     "vst1.u8    {q0}, [%2]!                    \n"
61     "subs       %3, %3, #16                    \n"  // 16 processed per loop
62     "bgt        1b                             \n"
63     : "+r"(src_ptr),          // %0
64       "+r"(src_stride),       // %1
65       "+r"(dst),              // %2
66       "+r"(dst_width)         // %3
67     :
68     : "q0", "q1", "q2", "q3"     // Clobber List
69    );
70 }
71 
ScaleRowDown4_NEON(const uint8 * src_ptr,ptrdiff_t,uint8 * dst_ptr,int dst_width)72 void ScaleRowDown4_NEON(const uint8* src_ptr, ptrdiff_t /* src_stride */,
73                         uint8* dst_ptr, int dst_width) {
74   asm volatile (
75     "1:                                        \n"
76     "vld2.u8    {d0, d1}, [%0]!                \n"
77     "vtrn.u8    d1, d0                         \n"
78     "vshrn.u16  d0, q0, #8                     \n"
79     "vst1.u32   {d0[1]}, [%1]!                 \n"
80     "subs       %2, #4                         \n"
81     "bgt        1b                             \n"
82     : "+r"(src_ptr),          // %0
83       "+r"(dst_ptr),          // %1
84       "+r"(dst_width)         // %2
85     :
86     : "q0", "q1", "memory", "cc"
87   );
88 }
89 
ScaleRowDown4Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst_ptr,int dst_width)90 void ScaleRowDown4Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
91                            uint8* dst_ptr, int dst_width) {
92   asm volatile (
93     "add        r4, %0, %3                     \n"
94     "add        r5, r4, %3                     \n"
95     "add        %3, r5, %3                     \n"
96     "1:                                        \n"
97     "vld1.u8    {q0}, [%0]!                    \n"   // load up 16x4
98     "vld1.u8    {q1}, [r4]!                    \n"
99     "vld1.u8    {q2}, [r5]!                    \n"
100     "vld1.u8    {q3}, [%3]!                    \n"
101     "vpaddl.u8  q0, q0                         \n"
102     "vpadal.u8  q0, q1                         \n"
103     "vpadal.u8  q0, q2                         \n"
104     "vpadal.u8  q0, q3                         \n"
105     "vpaddl.u16 q0, q0                         \n"
106     "vrshrn.u32 d0, q0, #4                     \n"   // divide by 16 w/rounding
107     "vmovn.u16  d0, q0                         \n"
108     "vst1.u32   {d0[0]}, [%1]!                 \n"
109     "subs       %2, #4                         \n"
110     "bgt        1b                             \n"
111     : "+r"(src_ptr),          // %0
112       "+r"(dst_ptr),          // %1
113       "+r"(dst_width)         // %2
114     : "r"(src_stride)         // %3
115     : "r4", "r5", "q0", "q1", "q2", "q3", "memory", "cc"
116   );
117 }
118 
119 // Down scale from 4 to 3 pixels. Use the neon multilane read/write
120 // to load up the every 4th pixel into a 4 different registers.
121 // Point samples 32 pixels to 24 pixels.
ScaleRowDown34_NEON(const uint8 * src_ptr,ptrdiff_t,uint8 * dst_ptr,int dst_width)122 void ScaleRowDown34_NEON(const uint8* src_ptr,
123                          ptrdiff_t /* src_stride */,
124                          uint8* dst_ptr, int dst_width) {
125   asm volatile (
126     "1:                                        \n"
127     "vld4.u8      {d0, d1, d2, d3}, [%0]!      \n" // src line 0
128     "vmov         d2, d3                       \n" // order d0, d1, d2
129     "vst3.u8      {d0, d1, d2}, [%1]!          \n"
130     "subs         %2, #24                      \n"
131     "bgt          1b                           \n"
132     : "+r"(src_ptr),          // %0
133       "+r"(dst_ptr),          // %1
134       "+r"(dst_width)         // %2
135     :
136     : "d0", "d1", "d2", "d3", "memory", "cc"
137   );
138 }
139 
ScaleRowDown34_0_Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst_ptr,int dst_width)140 void ScaleRowDown34_0_Int_NEON(const uint8* src_ptr,
141                                ptrdiff_t src_stride,
142                                uint8* dst_ptr, int dst_width) {
143   asm volatile (
144     "vmov.u8      d24, #3                      \n"
145     "add          %3, %0                       \n"
146     "1:                                        \n"
147     "vld4.u8      {d0, d1, d2, d3}, [%0]!      \n" // src line 0
148     "vld4.u8      {d4, d5, d6, d7}, [%3]!      \n" // src line 1
149 
150     // filter src line 0 with src line 1
151     // expand chars to shorts to allow for room
152     // when adding lines together
153     "vmovl.u8     q8, d4                       \n"
154     "vmovl.u8     q9, d5                       \n"
155     "vmovl.u8     q10, d6                      \n"
156     "vmovl.u8     q11, d7                      \n"
157 
158     // 3 * line_0 + line_1
159     "vmlal.u8     q8, d0, d24                  \n"
160     "vmlal.u8     q9, d1, d24                  \n"
161     "vmlal.u8     q10, d2, d24                 \n"
162     "vmlal.u8     q11, d3, d24                 \n"
163 
164     // (3 * line_0 + line_1) >> 2
165     "vqrshrn.u16  d0, q8, #2                   \n"
166     "vqrshrn.u16  d1, q9, #2                   \n"
167     "vqrshrn.u16  d2, q10, #2                  \n"
168     "vqrshrn.u16  d3, q11, #2                  \n"
169 
170     // a0 = (src[0] * 3 + s[1] * 1) >> 2
171     "vmovl.u8     q8, d1                       \n"
172     "vmlal.u8     q8, d0, d24                  \n"
173     "vqrshrn.u16  d0, q8, #2                   \n"
174 
175     // a1 = (src[1] * 1 + s[2] * 1) >> 1
176     "vrhadd.u8    d1, d1, d2                   \n"
177 
178     // a2 = (src[2] * 1 + s[3] * 3) >> 2
179     "vmovl.u8     q8, d2                       \n"
180     "vmlal.u8     q8, d3, d24                  \n"
181     "vqrshrn.u16  d2, q8, #2                   \n"
182 
183     "vst3.u8      {d0, d1, d2}, [%1]!          \n"
184 
185     "subs         %2, #24                      \n"
186     "bgt          1b                           \n"
187     : "+r"(src_ptr),          // %0
188       "+r"(dst_ptr),          // %1
189       "+r"(dst_width),        // %2
190       "+r"(src_stride)        // %3
191     :
192     : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "d24", "memory", "cc"
193   );
194 }
195 
ScaleRowDown34_1_Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst_ptr,int dst_width)196 void ScaleRowDown34_1_Int_NEON(const uint8* src_ptr,
197                                ptrdiff_t src_stride,
198                                uint8* dst_ptr, int dst_width) {
199   asm volatile (
200     "vmov.u8      d24, #3                      \n"
201     "add          %3, %0                       \n"
202     "1:                                        \n"
203     "vld4.u8      {d0, d1, d2, d3}, [%0]!      \n" // src line 0
204     "vld4.u8      {d4, d5, d6, d7}, [%3]!      \n" // src line 1
205 
206     // average src line 0 with src line 1
207     "vrhadd.u8    q0, q0, q2                   \n"
208     "vrhadd.u8    q1, q1, q3                   \n"
209 
210     // a0 = (src[0] * 3 + s[1] * 1) >> 2
211     "vmovl.u8     q3, d1                       \n"
212     "vmlal.u8     q3, d0, d24                  \n"
213     "vqrshrn.u16  d0, q3, #2                   \n"
214 
215     // a1 = (src[1] * 1 + s[2] * 1) >> 1
216     "vrhadd.u8    d1, d1, d2                   \n"
217 
218     // a2 = (src[2] * 1 + s[3] * 3) >> 2
219     "vmovl.u8     q3, d2                       \n"
220     "vmlal.u8     q3, d3, d24                  \n"
221     "vqrshrn.u16  d2, q3, #2                   \n"
222 
223     "vst3.u8      {d0, d1, d2}, [%1]!          \n"
224 
225     "subs         %2, #24                      \n"
226     "bgt          1b                           \n"
227     : "+r"(src_ptr),          // %0
228       "+r"(dst_ptr),          // %1
229       "+r"(dst_width),        // %2
230       "+r"(src_stride)        // %3
231     :
232     : "r4", "q0", "q1", "q2", "q3", "d24", "memory", "cc"
233   );
234 }
235 
236 #define HAS_SCALEROWDOWN38_NEON
237 const uvec8 kShuf38 =
238   { 0, 3, 6, 8, 11, 14, 16, 19, 22, 24, 27, 30, 0, 0, 0, 0 };
239 const uvec8 kShuf38_2 =
240   { 0, 8, 16, 2, 10, 17, 4, 12, 18, 6, 14, 19, 0, 0, 0, 0 };
241 const vec16 kMult38_Div6 =
242   { 65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12,
243     65536 / 12, 65536 / 12, 65536 / 12, 65536 / 12 };
244 const vec16 kMult38_Div9 =
245   { 65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18,
246     65536 / 18, 65536 / 18, 65536 / 18, 65536 / 18 };
247 
248 // 32 -> 12
ScaleRowDown38_NEON(const uint8 * src_ptr,ptrdiff_t,uint8 * dst_ptr,int dst_width)249 void ScaleRowDown38_NEON(const uint8* src_ptr,
250                          ptrdiff_t /* src_stride */,
251                          uint8* dst_ptr, int dst_width) {
252   asm volatile (
253     "vld1.u8      {q3}, [%3]                   \n"
254     "1:                                        \n"
255     "vld1.u8      {d0, d1, d2, d3}, [%0]!      \n"
256     "vtbl.u8      d4, {d0, d1, d2, d3}, d6     \n"
257     "vtbl.u8      d5, {d0, d1, d2, d3}, d7     \n"
258     "vst1.u8      {d4}, [%1]!                  \n"
259     "vst1.u32     {d5[0]}, [%1]!               \n"
260     "subs         %2, #12                      \n"
261     "bgt          1b                           \n"
262     : "+r"(src_ptr),          // %0
263       "+r"(dst_ptr),          // %1
264       "+r"(dst_width)         // %2
265     : "r"(&kShuf38)           // %3
266     : "d0", "d1", "d2", "d3", "d4", "d5", "memory", "cc"
267   );
268 }
269 
270 // 32x3 -> 12x1
ScaleRowDown38_3_Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst_ptr,int dst_width)271 void OMITFP ScaleRowDown38_3_Int_NEON(const uint8* src_ptr,
272                                       ptrdiff_t src_stride,
273                                       uint8* dst_ptr, int dst_width) {
274   asm volatile (
275     "vld1.u16     {q13}, [%4]                  \n"
276     "vld1.u8      {q14}, [%5]                  \n"
277     "vld1.u8      {q15}, [%6]                  \n"
278     "add          r4, %0, %3, lsl #1           \n"
279     "add          %3, %0                       \n"
280     "1:                                        \n"
281 
282     // d0 = 00 40 01 41 02 42 03 43
283     // d1 = 10 50 11 51 12 52 13 53
284     // d2 = 20 60 21 61 22 62 23 63
285     // d3 = 30 70 31 71 32 72 33 73
286     "vld4.u8      {d0, d1, d2, d3}, [%0]!      \n"
287     "vld4.u8      {d4, d5, d6, d7}, [%3]!      \n"
288     "vld4.u8      {d16, d17, d18, d19}, [r4]!  \n"
289 
290     // Shuffle the input data around to get align the data
291     //  so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
292     // d0 = 00 10 01 11 02 12 03 13
293     // d1 = 40 50 41 51 42 52 43 53
294     "vtrn.u8      d0, d1                       \n"
295     "vtrn.u8      d4, d5                       \n"
296     "vtrn.u8      d16, d17                     \n"
297 
298     // d2 = 20 30 21 31 22 32 23 33
299     // d3 = 60 70 61 71 62 72 63 73
300     "vtrn.u8      d2, d3                       \n"
301     "vtrn.u8      d6, d7                       \n"
302     "vtrn.u8      d18, d19                     \n"
303 
304     // d0 = 00+10 01+11 02+12 03+13
305     // d2 = 40+50 41+51 42+52 43+53
306     "vpaddl.u8    q0, q0                       \n"
307     "vpaddl.u8    q2, q2                       \n"
308     "vpaddl.u8    q8, q8                       \n"
309 
310     // d3 = 60+70 61+71 62+72 63+73
311     "vpaddl.u8    d3, d3                       \n"
312     "vpaddl.u8    d7, d7                       \n"
313     "vpaddl.u8    d19, d19                     \n"
314 
315     // combine source lines
316     "vadd.u16     q0, q2                       \n"
317     "vadd.u16     q0, q8                       \n"
318     "vadd.u16     d4, d3, d7                   \n"
319     "vadd.u16     d4, d19                      \n"
320 
321     // dst_ptr[3] = (s[6 + st * 0] + s[7 + st * 0]
322     //             + s[6 + st * 1] + s[7 + st * 1]
323     //             + s[6 + st * 2] + s[7 + st * 2]) / 6
324     "vqrdmulh.s16 q2, q2, q13                  \n"
325     "vmovn.u16    d4, q2                       \n"
326 
327     // Shuffle 2,3 reg around so that 2 can be added to the
328     //  0,1 reg and 3 can be added to the 4,5 reg. This
329     //  requires expanding from u8 to u16 as the 0,1 and 4,5
330     //  registers are already expanded. Then do transposes
331     //  to get aligned.
332     // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
333     "vmovl.u8     q1, d2                       \n"
334     "vmovl.u8     q3, d6                       \n"
335     "vmovl.u8     q9, d18                      \n"
336 
337     // combine source lines
338     "vadd.u16     q1, q3                       \n"
339     "vadd.u16     q1, q9                       \n"
340 
341     // d4 = xx 20 xx 30 xx 22 xx 32
342     // d5 = xx 21 xx 31 xx 23 xx 33
343     "vtrn.u32     d2, d3                       \n"
344 
345     // d4 = xx 20 xx 21 xx 22 xx 23
346     // d5 = xx 30 xx 31 xx 32 xx 33
347     "vtrn.u16     d2, d3                       \n"
348 
349     // 0+1+2, 3+4+5
350     "vadd.u16     q0, q1                       \n"
351 
352     // Need to divide, but can't downshift as the the value
353     //  isn't a power of 2. So multiply by 65536 / n
354     //  and take the upper 16 bits.
355     "vqrdmulh.s16 q0, q0, q15                  \n"
356 
357     // Align for table lookup, vtbl requires registers to
358     //  be adjacent
359     "vmov.u8      d2, d4                       \n"
360 
361     "vtbl.u8      d3, {d0, d1, d2}, d28        \n"
362     "vtbl.u8      d4, {d0, d1, d2}, d29        \n"
363 
364     "vst1.u8      {d3}, [%1]!                  \n"
365     "vst1.u32     {d4[0]}, [%1]!               \n"
366     "subs         %2, #12                      \n"
367     "bgt          1b                           \n"
368     : "+r"(src_ptr),          // %0
369       "+r"(dst_ptr),          // %1
370       "+r"(dst_width),        // %2
371       "+r"(src_stride)        // %3
372     : "r"(&kMult38_Div6),     // %4
373       "r"(&kShuf38_2),        // %5
374       "r"(&kMult38_Div9)      // %6
375     : "r4", "q0", "q1", "q2", "q3", "q8", "q9",
376       "q13", "q14", "q15", "memory", "cc"
377   );
378 }
379 
380 // 32x2 -> 12x1
ScaleRowDown38_2_Int_NEON(const uint8 * src_ptr,ptrdiff_t src_stride,uint8 * dst_ptr,int dst_width)381 void ScaleRowDown38_2_Int_NEON(const uint8* src_ptr,
382                                ptrdiff_t src_stride,
383                                uint8* dst_ptr, int dst_width) {
384   asm volatile (
385     "vld1.u16     {q13}, [%4]                  \n"
386     "vld1.u8      {q14}, [%5]                  \n"
387     "add          %3, %0                       \n"
388     "1:                                        \n"
389 
390     // d0 = 00 40 01 41 02 42 03 43
391     // d1 = 10 50 11 51 12 52 13 53
392     // d2 = 20 60 21 61 22 62 23 63
393     // d3 = 30 70 31 71 32 72 33 73
394     "vld4.u8      {d0, d1, d2, d3}, [%0]!      \n"
395     "vld4.u8      {d4, d5, d6, d7}, [%3]!      \n"
396 
397     // Shuffle the input data around to get align the data
398     //  so adjacent data can be added. 0,1 - 2,3 - 4,5 - 6,7
399     // d0 = 00 10 01 11 02 12 03 13
400     // d1 = 40 50 41 51 42 52 43 53
401     "vtrn.u8      d0, d1                       \n"
402     "vtrn.u8      d4, d5                       \n"
403 
404     // d2 = 20 30 21 31 22 32 23 33
405     // d3 = 60 70 61 71 62 72 63 73
406     "vtrn.u8      d2, d3                       \n"
407     "vtrn.u8      d6, d7                       \n"
408 
409     // d0 = 00+10 01+11 02+12 03+13
410     // d2 = 40+50 41+51 42+52 43+53
411     "vpaddl.u8    q0, q0                       \n"
412     "vpaddl.u8    q2, q2                       \n"
413 
414     // d3 = 60+70 61+71 62+72 63+73
415     "vpaddl.u8    d3, d3                       \n"
416     "vpaddl.u8    d7, d7                       \n"
417 
418     // combine source lines
419     "vadd.u16     q0, q2                       \n"
420     "vadd.u16     d4, d3, d7                   \n"
421 
422     // dst_ptr[3] = (s[6] + s[7] + s[6+st] + s[7+st]) / 4
423     "vqrshrn.u16  d4, q2, #2                   \n"
424 
425     // Shuffle 2,3 reg around so that 2 can be added to the
426     //  0,1 reg and 3 can be added to the 4,5 reg. This
427     //  requires expanding from u8 to u16 as the 0,1 and 4,5
428     //  registers are already expanded. Then do transposes
429     //  to get aligned.
430     // q2 = xx 20 xx 30 xx 21 xx 31 xx 22 xx 32 xx 23 xx 33
431     "vmovl.u8     q1, d2                       \n"
432     "vmovl.u8     q3, d6                       \n"
433 
434     // combine source lines
435     "vadd.u16     q1, q3                       \n"
436 
437     // d4 = xx 20 xx 30 xx 22 xx 32
438     // d5 = xx 21 xx 31 xx 23 xx 33
439     "vtrn.u32     d2, d3                       \n"
440 
441     // d4 = xx 20 xx 21 xx 22 xx 23
442     // d5 = xx 30 xx 31 xx 32 xx 33
443     "vtrn.u16     d2, d3                       \n"
444 
445     // 0+1+2, 3+4+5
446     "vadd.u16     q0, q1                       \n"
447 
448     // Need to divide, but can't downshift as the the value
449     //  isn't a power of 2. So multiply by 65536 / n
450     //  and take the upper 16 bits.
451     "vqrdmulh.s16 q0, q0, q13                  \n"
452 
453     // Align for table lookup, vtbl requires registers to
454     //  be adjacent
455     "vmov.u8      d2, d4                       \n"
456 
457     "vtbl.u8      d3, {d0, d1, d2}, d28        \n"
458     "vtbl.u8      d4, {d0, d1, d2}, d29        \n"
459 
460     "vst1.u8      {d3}, [%1]!                  \n"
461     "vst1.u32     {d4[0]}, [%1]!               \n"
462     "subs         %2, #12                      \n"
463     "bgt          1b                           \n"
464     : "+r"(src_ptr),       // %0
465       "+r"(dst_ptr),       // %1
466       "+r"(dst_width),     // %2
467       "+r"(src_stride)     // %3
468     : "r"(&kMult38_Div6),  // %4
469       "r"(&kShuf38_2)      // %5
470     : "q0", "q1", "q2", "q3", "q13", "q14", "memory", "cc"
471   );
472 }
473 
474 // 16x2 -> 16x1
ScaleFilterRows_NEON(uint8 * dst_ptr,const uint8 * src_ptr,ptrdiff_t src_stride,int dst_width,int source_y_fraction)475 void ScaleFilterRows_NEON(uint8* dst_ptr,
476                           const uint8* src_ptr, ptrdiff_t src_stride,
477                           int dst_width, int source_y_fraction) {
478   asm volatile (
479     "cmp          %4, #0                       \n"
480     "beq          2f                           \n"
481     "add          %2, %1                       \n"
482     "cmp          %4, #128                     \n"
483     "beq          3f                           \n"
484 
485     "vdup.8       d5, %4                       \n"
486     "rsb          %4, #256                     \n"
487     "vdup.8       d4, %4                       \n"
488     "1:                                        \n"
489     "vld1.u8      {q0}, [%1]!                  \n"
490     "vld1.u8      {q1}, [%2]!                  \n"
491     "subs         %3, #16                      \n"
492     "vmull.u8     q13, d0, d4                  \n"
493     "vmull.u8     q14, d1, d4                  \n"
494     "vmlal.u8     q13, d2, d5                  \n"
495     "vmlal.u8     q14, d3, d5                  \n"
496     "vrshrn.u16   d0, q13, #8                  \n"
497     "vrshrn.u16   d1, q14, #8                  \n"
498     "vst1.u8      {q0}, [%0]!                  \n"
499     "bgt          1b                           \n"
500     "b            4f                           \n"
501 
502     "2:                                        \n"
503     "vld1.u8      {q0}, [%1]!                  \n"
504     "subs         %3, #16                      \n"
505     "vst1.u8      {q0}, [%0]!                  \n"
506     "bgt          2b                           \n"
507     "b            4f                           \n"
508 
509     "3:                                        \n"
510     "vld1.u8      {q0}, [%1]!                  \n"
511     "vld1.u8      {q1}, [%2]!                  \n"
512     "subs         %3, #16                      \n"
513     "vrhadd.u8    q0, q1                       \n"
514     "vst1.u8      {q0}, [%0]!                  \n"
515     "bgt          3b                           \n"
516     "4:                                        \n"
517     "vst1.u8      {d1[7]}, [%0]                \n"
518     : "+r"(dst_ptr),          // %0
519       "+r"(src_ptr),          // %1
520       "+r"(src_stride),       // %2
521       "+r"(dst_width),        // %3
522       "+r"(source_y_fraction) // %4
523     :
524     : "q0", "q1", "d4", "d5", "q13", "q14", "memory", "cc"
525   );
526 }
527 
528 #endif  // __ARM_NEON__
529 
530 #ifdef __cplusplus
531 }  // extern "C"
532 }  // namespace libyuv
533 #endif
534 
535