Home | History | Annotate | Download | only in source
      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 
     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 
     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 
     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 
     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.
    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 
    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 
    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
    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
    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
    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
    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