jsimd_arm_neon.S revision 82bd52196d0ee3eda47bfbb3da8fb886739484bf
1/*
2 * ARM NEON optimizations for libjpeg-turbo
3 *
4 * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
5 * All rights reserved.
6 * Contact: Alexander Bokovoy <alexander.bokovoy@nokia.com>
7 *
8 * This software is provided 'as-is', without any express or implied
9 * warranty.  In no event will the authors be held liable for any damages
10 * arising from the use of this software.
11 *
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, subject to the following restrictions:
15 *
16 * 1. The origin of this software must not be misrepresented; you must not
17 *    claim that you wrote the original software. If you use this software
18 *    in a product, an acknowledgment in the product documentation would be
19 *    appreciated but is not required.
20 * 2. Altered source versions must be plainly marked as such, and must not be
21 *    misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source distribution.
23 */
24
25#if defined(__linux__) && defined(__ELF__)
26.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
27#endif
28
29.text
30.fpu neon
31.arch armv7a
32.object_arch armv4
33.arm
34
35
36#define RESPECT_STRICT_ALIGNMENT 1
37
38/*****************************************************************************/
39
40/* Supplementary macro for setting function attributes */
41.macro asm_function fname
42#ifdef __APPLE__
43    .func _\fname
44    .globl _\fname
45_\fname:
46#else
47    .func \fname
48    .global \fname
49#ifdef __ELF__
50    .hidden \fname
51    .type \fname, %function
52#endif
53\fname:
54#endif
55.endm
56
57/* Transpose a block of 4x4 coefficients in four 64-bit registers */
58.macro transpose_4x4 x0, x1, x2, x3
59    vtrn.16 \x0, \x1
60    vtrn.16 \x2, \x3
61    vtrn.32 \x0, \x2
62    vtrn.32 \x1, \x3
63.endm
64
65/*****************************************************************************/
66
67/*
68 * jsimd_idct_ifast_neon
69 *
70 * This function contains a fast, not so accurate integer implementation of
71 * the inverse DCT (Discrete Cosine Transform). It uses the same calculations
72 * and produces exactly the same output as IJG's original 'jpeg_idct_ifast'
73 * function from jidctfst.c
74 *
75 * Normally 1-D AAN DCT needs 5 multiplications and 29 additions.
76 * But in ARM NEON case some extra additions are required because VQDMULH
77 * instruction can't handle the constants larger than 1. So the expressions
78 * like "x * 1.082392200" have to be converted to "x * 0.082392200 + x",
79 * which introduces an extra addition. Overall, there are 6 extra additions
80 * per 1-D IDCT pass, totalling to 5 VQDMULH and 35 VADD/VSUB instructions.
81 */
82
83#define XFIX_1_082392200 d0[0]
84#define XFIX_1_414213562 d0[1]
85#define XFIX_1_847759065 d0[2]
86#define XFIX_2_613125930 d0[3]
87
88.balign 16
89jsimd_idct_ifast_neon_consts:
90    .short (277 * 128 - 256 * 128) /* XFIX_1_082392200 */
91    .short (362 * 128 - 256 * 128) /* XFIX_1_414213562 */
92    .short (473 * 128 - 256 * 128) /* XFIX_1_847759065 */
93    .short (669 * 128 - 512 * 128) /* XFIX_2_613125930 */
94
95asm_function jsimd_idct_ifast_neon
96
97    DCT_TABLE       .req r0
98    COEF_BLOCK      .req r1
99    OUTPUT_BUF      .req r2
100    OUTPUT_COL      .req r3
101    TMP1            .req r0
102    TMP2            .req r1
103    TMP3            .req r2
104    TMP4            .req ip
105
106    /* Load and dequantize coefficients into NEON registers
107     * with the following allocation:
108     *       0 1 2 3 | 4 5 6 7
109     *      ---------+--------
110     *   0 | d16     | d17     ( q8  )
111     *   1 | d18     | d19     ( q9  )
112     *   2 | d20     | d21     ( q10 )
113     *   3 | d22     | d23     ( q11 )
114     *   4 | d24     | d25     ( q12 )
115     *   5 | d26     | d27     ( q13 )
116     *   6 | d28     | d29     ( q14 )
117     *   7 | d30     | d31     ( q15 )
118     */
119    adr             ip, jsimd_idct_ifast_neon_consts
120    vld1.16         {d16, d17, d18, d19}, [COEF_BLOCK, :128]!
121    vld1.16         {d0, d1, d2, d3}, [DCT_TABLE, :128]!
122    vld1.16         {d20, d21, d22, d23}, [COEF_BLOCK, :128]!
123    vmul.s16        q8,  q8,  q0
124    vld1.16         {d4, d5, d6, d7}, [DCT_TABLE, :128]!
125    vmul.s16        q9,  q9,  q1
126    vld1.16         {d24, d25, d26, d27}, [COEF_BLOCK, :128]!
127    vmul.s16        q10, q10, q2
128    vld1.16         {d0, d1, d2, d3}, [DCT_TABLE, :128]!
129    vmul.s16        q11, q11, q3
130    vld1.16         {d28, d29, d30, d31}, [COEF_BLOCK, :128]
131    vmul.s16        q12, q12, q0
132    vld1.16         {d4, d5, d6, d7}, [DCT_TABLE, :128]!
133    vmul.s16        q14, q14, q2
134    vmul.s16        q13, q13, q1
135    vld1.16         {d0}, [ip, :64] /* load constants */
136    vmul.s16        q15, q15, q3
137    vpush           {d8-d13}        /* save NEON registers */
138    /* 1-D IDCT, pass 1 */
139    vsub.s16        q2,  q10, q14
140    vadd.s16        q14, q10, q14
141    vsub.s16        q1,  q11, q13
142    vadd.s16        q13, q11, q13
143    vsub.s16        q5,  q9,  q15
144    vadd.s16        q15, q9,  q15
145    vqdmulh.s16     q4,  q2,  XFIX_1_414213562
146    vqdmulh.s16     q6,  q1,  XFIX_2_613125930
147    vadd.s16        q3,  q1,  q1
148    vsub.s16        q1,  q5,  q1
149    vadd.s16        q10, q2,  q4
150    vqdmulh.s16     q4,  q1,  XFIX_1_847759065
151    vsub.s16        q2,  q15, q13
152    vadd.s16        q3,  q3,  q6
153    vqdmulh.s16     q6,  q2,  XFIX_1_414213562
154    vadd.s16        q1,  q1,  q4
155    vqdmulh.s16     q4,  q5,  XFIX_1_082392200
156    vsub.s16        q10, q10, q14
157    vadd.s16        q2,  q2,  q6
158    vsub.s16        q6,  q8,  q12
159    vadd.s16        q12, q8,  q12
160    vadd.s16        q9,  q5,  q4
161    vadd.s16        q5,  q6,  q10
162    vsub.s16        q10, q6,  q10
163    vadd.s16        q6,  q15, q13
164    vadd.s16        q8,  q12, q14
165    vsub.s16        q3,  q6,  q3
166    vsub.s16        q12, q12, q14
167    vsub.s16        q3,  q3,  q1
168    vsub.s16        q1,  q9,  q1
169    vadd.s16        q2,  q3,  q2
170    vsub.s16        q15, q8,  q6
171    vadd.s16        q1,  q1,  q2
172    vadd.s16        q8,  q8,  q6
173    vadd.s16        q14, q5,  q3
174    vsub.s16        q9,  q5,  q3
175    vsub.s16        q13, q10, q2
176    vadd.s16        q10, q10, q2
177      /* Transpose */
178      vtrn.16         q8,  q9
179    vsub.s16        q11, q12, q1
180      vtrn.16         q14, q15
181    vadd.s16        q12, q12, q1
182      vtrn.16         q10, q11
183      vtrn.16         q12, q13
184      vtrn.32         q9,  q11
185      vtrn.32         q12, q14
186      vtrn.32         q8,  q10
187      vtrn.32         q13, q15
188      vswp            d28, d21
189      vswp            d26, d19
190    /* 1-D IDCT, pass 2 */
191    vsub.s16        q2,  q10, q14
192      vswp            d30, d23
193    vadd.s16        q14, q10, q14
194      vswp            d24, d17
195    vsub.s16        q1,  q11, q13
196    vadd.s16        q13, q11, q13
197    vsub.s16        q5,  q9,  q15
198    vadd.s16        q15, q9,  q15
199    vqdmulh.s16     q4,  q2,  XFIX_1_414213562
200    vqdmulh.s16     q6,  q1,  XFIX_2_613125930
201    vadd.s16        q3,  q1,  q1
202    vsub.s16        q1,  q5,  q1
203    vadd.s16        q10, q2,  q4
204    vqdmulh.s16     q4,  q1,  XFIX_1_847759065
205    vsub.s16        q2,  q15, q13
206    vadd.s16        q3,  q3,  q6
207    vqdmulh.s16     q6,  q2,  XFIX_1_414213562
208    vadd.s16        q1,  q1,  q4
209    vqdmulh.s16     q4,  q5,  XFIX_1_082392200
210    vsub.s16        q10, q10, q14
211    vadd.s16        q2,  q2,  q6
212    vsub.s16        q6,  q8,  q12
213    vadd.s16        q12, q8,  q12
214    vadd.s16        q9,  q5,  q4
215    vadd.s16        q5,  q6,  q10
216    vsub.s16        q10, q6,  q10
217    vadd.s16        q6,  q15, q13
218    vadd.s16        q8,  q12, q14
219    vsub.s16        q3,  q6,  q3
220    vsub.s16        q12, q12, q14
221    vsub.s16        q3,  q3,  q1
222    vsub.s16        q1,  q9,  q1
223    vadd.s16        q2,  q3,  q2
224    vsub.s16        q15, q8,  q6
225    vadd.s16        q1,  q1,  q2
226    vadd.s16        q8,  q8,  q6
227    vadd.s16        q14, q5,  q3
228    vsub.s16        q9,  q5,  q3
229    vsub.s16        q13, q10, q2
230    vpop            {d8-d13}        /* restore NEON registers */
231    vadd.s16        q10, q10, q2
232      /* Transpose */
233      vtrn.16         q8,  q9
234    vsub.s16        q11, q12, q1
235      vtrn.16         q14, q15
236    vadd.s16        q12, q12, q1
237      vtrn.16         q10, q11
238      vtrn.16         q12, q13
239    /* Descale and range limit */
240    vmov.s16        q0,  #(0x80 << 5)
241      vtrn.32         q9,  q11
242      vtrn.32         q12, q14
243      vtrn.32         q8,  q10
244      vtrn.32         q13, q15
245      vswp            d24, d17
246      vswp            d26, d19
247    vqadd.s16       q8,  q8,  q0
248      vswp            d28, d21
249    vqadd.s16       q9,  q9,  q0
250      vswp            d30, d23
251    vqadd.s16       q10, q10, q0
252    vqadd.s16       q11, q11, q0
253      /* Store results to the output buffer */
254      ldmia           OUTPUT_BUF!, {TMP1, TMP2}
255      add             TMP1, TMP1, OUTPUT_COL
256      add             TMP2, TMP2, OUTPUT_COL
257    vqshrun.s16     d16, q8,  #5
258    vqshrun.s16     d17, q9,  #5
259    vqshrun.s16     d18, q10, #5
260    vqshrun.s16     d19, q11, #5
261      vst1.8          {d16}, [TMP1]
262    vqadd.s16       q12, q12, q0
263    vqadd.s16       q13, q13, q0
264      vst1.8          {d17}, [TMP2]
265    vqadd.s16       q14, q14, q0
266    vqadd.s16       q15, q15, q0
267      ldmia           OUTPUT_BUF!, {TMP1, TMP2}
268      add             TMP1, TMP1, OUTPUT_COL
269      add             TMP2, TMP2, OUTPUT_COL
270      vst1.8          {d18}, [TMP1]
271    vqshrun.s16     d20, q12, #5
272    vqshrun.s16     d21, q13, #5
273      vst1.8          {d19}, [TMP2]
274    vqshrun.s16     d22, q14, #5
275      ldmia           OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4}
276      add             TMP1, TMP1, OUTPUT_COL
277      add             TMP2, TMP2, OUTPUT_COL
278      add             TMP3, TMP3, OUTPUT_COL
279      add             TMP4, TMP4, OUTPUT_COL
280      vst1.8          {d20}, [TMP1]
281    vqshrun.s16     d23, q15, #5
282      vst1.8          {d21}, [TMP2]
283      vst1.8          {d22}, [TMP3]
284      vst1.8          {d23}, [TMP4]
285    bx              lr
286
287    .unreq          DCT_TABLE
288    .unreq          COEF_BLOCK
289    .unreq          OUTPUT_BUF
290    .unreq          OUTPUT_COL
291    .unreq          TMP1
292    .unreq          TMP2
293    .unreq          TMP3
294    .unreq          TMP4
295.endfunc
296
297/*****************************************************************************/
298
299/*
300 * jsimd_idct_4x4_neon
301 *
302 * This function contains inverse-DCT code for getting reduced-size
303 * 4x4 pixels output from an 8x8 DCT block. It uses the same  calculations
304 * and produces exactly the same output as IJG's original 'jpeg_idct_4x4'
305 * function from jpeg-6b (jidctred.c).
306 *
307 * NOTE: jpeg-8 has an improved implementation of 4x4 inverse-DCT, which
308 *       requires much less arithmetic operations and hence should be faster.
309 *       The primary purpose of this particular NEON optimized function is
310 *       bit exact compatibility with jpeg-6b.
311 *
312 * TODO: a bit better instructions scheduling can be achieved by expanding
313 *       idct_helper/transpose_4x4 macros and reordering instructions,
314 *       but readability will suffer somewhat.
315 */
316
317#define CONST_BITS  13
318
319#define FIX_0_211164243  (1730)  /* FIX(0.211164243) */
320#define FIX_0_509795579  (4176)  /* FIX(0.509795579) */
321#define FIX_0_601344887  (4926)  /* FIX(0.601344887) */
322#define FIX_0_720959822  (5906)  /* FIX(0.720959822) */
323#define FIX_0_765366865  (6270)  /* FIX(0.765366865) */
324#define FIX_0_850430095  (6967)  /* FIX(0.850430095) */
325#define FIX_0_899976223  (7373)  /* FIX(0.899976223) */
326#define FIX_1_061594337  (8697)  /* FIX(1.061594337) */
327#define FIX_1_272758580  (10426) /* FIX(1.272758580) */
328#define FIX_1_451774981  (11893) /* FIX(1.451774981) */
329#define FIX_1_847759065  (15137) /* FIX(1.847759065) */
330#define FIX_2_172734803  (17799) /* FIX(2.172734803) */
331#define FIX_2_562915447  (20995) /* FIX(2.562915447) */
332#define FIX_3_624509785  (29692) /* FIX(3.624509785) */
333
334.balign 16
335jsimd_idct_4x4_neon_consts:
336    .short     FIX_1_847759065     /* d0[0] */
337    .short     -FIX_0_765366865    /* d0[1] */
338    .short     -FIX_0_211164243    /* d0[2] */
339    .short     FIX_1_451774981     /* d0[3] */
340    .short     -FIX_2_172734803    /* d1[0] */
341    .short     FIX_1_061594337     /* d1[1] */
342    .short     -FIX_0_509795579    /* d1[2] */
343    .short     -FIX_0_601344887    /* d1[3] */
344    .short     FIX_0_899976223     /* d2[0] */
345    .short     FIX_2_562915447     /* d2[1] */
346    .short     1 << (CONST_BITS+1) /* d2[2] */
347    .short     0                   /* d2[3] */
348
349.macro idct_helper x4, x6, x8, x10, x12, x14, x16, shift, y26, y27, y28, y29
350    vmull.s16       q14, \x4,  d2[2]
351    vmlal.s16       q14, \x8,  d0[0]
352    vmlal.s16       q14, \x14, d0[1]
353
354    vmull.s16       q13, \x16, d1[2]
355    vmlal.s16       q13, \x12, d1[3]
356    vmlal.s16       q13, \x10, d2[0]
357    vmlal.s16       q13, \x6,  d2[1]
358
359    vmull.s16       q15, \x4,  d2[2]
360    vmlsl.s16       q15, \x8,  d0[0]
361    vmlsl.s16       q15, \x14, d0[1]
362
363    vmull.s16       q12, \x16, d0[2]
364    vmlal.s16       q12, \x12, d0[3]
365    vmlal.s16       q12, \x10, d1[0]
366    vmlal.s16       q12, \x6,  d1[1]
367
368    vadd.s32        q10, q14, q13
369    vsub.s32        q14, q14, q13
370
371.if \shift > 16
372    vrshr.s32       q10,  q10, #\shift
373    vrshr.s32       q14,  q14, #\shift
374    vmovn.s32       \y26, q10
375    vmovn.s32       \y29, q14
376.else
377    vrshrn.s32      \y26, q10, #\shift
378    vrshrn.s32      \y29, q14, #\shift
379.endif
380
381    vadd.s32        q10, q15, q12
382    vsub.s32        q15, q15, q12
383
384.if \shift > 16
385    vrshr.s32       q10,  q10, #\shift
386    vrshr.s32       q15,  q15, #\shift
387    vmovn.s32       \y27, q10
388    vmovn.s32       \y28, q15
389.else
390    vrshrn.s32      \y27, q10, #\shift
391    vrshrn.s32      \y28, q15, #\shift
392.endif
393
394.endm
395
396asm_function jsimd_idct_4x4_neon
397
398    DCT_TABLE       .req r0
399    COEF_BLOCK      .req r1
400    OUTPUT_BUF      .req r2
401    OUTPUT_COL      .req r3
402    TMP1            .req r0
403    TMP2            .req r1
404    TMP3            .req r2
405    TMP4            .req ip
406
407    vpush           {d8-d15}
408
409    /* Load constants (d3 is just used for padding) */
410    adr             TMP4, jsimd_idct_4x4_neon_consts
411    vld1.16         {d0, d1, d2, d3}, [TMP4, :128]
412
413    /* Load all COEF_BLOCK into NEON registers with the following allocation:
414     *       0 1 2 3 | 4 5 6 7
415     *      ---------+--------
416     *   0 | d4      | d5
417     *   1 | d6      | d7
418     *   2 | d8      | d9
419     *   3 | d10     | d11
420     *   4 | -       | -
421     *   5 | d12     | d13
422     *   6 | d14     | d15
423     *   7 | d16     | d17
424     */
425    vld1.16         {d4, d5, d6, d7}, [COEF_BLOCK, :128]!
426    vld1.16         {d8, d9, d10, d11}, [COEF_BLOCK, :128]!
427    add COEF_BLOCK, COEF_BLOCK, #16
428    vld1.16         {d12, d13, d14, d15}, [COEF_BLOCK, :128]!
429    vld1.16         {d16, d17}, [COEF_BLOCK, :128]!
430    /* dequantize */
431    vld1.16         {d18, d19, d20, d21}, [DCT_TABLE, :128]!
432    vmul.s16        q2, q2, q9
433    vld1.16         {d22, d23, d24, d25}, [DCT_TABLE, :128]!
434    vmul.s16        q3, q3, q10
435    vmul.s16        q4, q4, q11
436    add             DCT_TABLE, DCT_TABLE, #16
437    vld1.16         {d26, d27, d28, d29}, [DCT_TABLE, :128]!
438    vmul.s16        q5, q5, q12
439    vmul.s16        q6, q6, q13
440    vld1.16         {d30, d31}, [DCT_TABLE, :128]!
441    vmul.s16        q7, q7, q14
442    vmul.s16        q8, q8, q15
443
444    /* Pass 1 */
445    idct_helper     d4, d6, d8, d10, d12, d14, d16, 12, d4, d6, d8, d10
446    transpose_4x4   d4, d6, d8, d10
447    idct_helper     d5, d7, d9, d11, d13, d15, d17, 12, d5, d7, d9, d11
448    transpose_4x4   d5, d7, d9, d11
449
450    /* Pass 2 */
451    idct_helper     d4, d6, d8, d10, d7, d9, d11, 19, d26, d27, d28, d29
452    transpose_4x4   d26, d27, d28, d29
453
454    /* Range limit */
455    vmov.u16        q15, #0x80
456    vadd.s16        q13, q13, q15
457    vadd.s16        q14, q14, q15
458    vqmovun.s16     d26, q13
459    vqmovun.s16     d27, q14
460
461    /* Store results to the output buffer */
462    ldmia           OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4}
463    add             TMP1, TMP1, OUTPUT_COL
464    add             TMP2, TMP2, OUTPUT_COL
465    add             TMP3, TMP3, OUTPUT_COL
466    add             TMP4, TMP4, OUTPUT_COL
467
468#if defined(__ARMEL__) && !RESPECT_STRICT_ALIGNMENT
469    /* We can use much less instructions on little endian systems if the
470     * OS kernel is not configured to trap unaligned memory accesses
471     */
472    vst1.32         {d26[0]}, [TMP1]!
473    vst1.32         {d27[0]}, [TMP3]!
474    vst1.32         {d26[1]}, [TMP2]!
475    vst1.32         {d27[1]}, [TMP4]!
476#else
477    vst1.8          {d26[0]}, [TMP1]!
478    vst1.8          {d27[0]}, [TMP3]!
479    vst1.8          {d26[1]}, [TMP1]!
480    vst1.8          {d27[1]}, [TMP3]!
481    vst1.8          {d26[2]}, [TMP1]!
482    vst1.8          {d27[2]}, [TMP3]!
483    vst1.8          {d26[3]}, [TMP1]!
484    vst1.8          {d27[3]}, [TMP3]!
485
486    vst1.8          {d26[4]}, [TMP2]!
487    vst1.8          {d27[4]}, [TMP4]!
488    vst1.8          {d26[5]}, [TMP2]!
489    vst1.8          {d27[5]}, [TMP4]!
490    vst1.8          {d26[6]}, [TMP2]!
491    vst1.8          {d27[6]}, [TMP4]!
492    vst1.8          {d26[7]}, [TMP2]!
493    vst1.8          {d27[7]}, [TMP4]!
494#endif
495
496    vpop            {d8-d15}
497    bx              lr
498
499    .unreq          DCT_TABLE
500    .unreq          COEF_BLOCK
501    .unreq          OUTPUT_BUF
502    .unreq          OUTPUT_COL
503    .unreq          TMP1
504    .unreq          TMP2
505    .unreq          TMP3
506    .unreq          TMP4
507.endfunc
508
509.purgem idct_helper
510
511/*****************************************************************************/
512
513/*
514 * jsimd_idct_2x2_neon
515 *
516 * This function contains inverse-DCT code for getting reduced-size
517 * 2x2 pixels output from an 8x8 DCT block. It uses the same  calculations
518 * and produces exactly the same output as IJG's original 'jpeg_idct_2x2'
519 * function from jpeg-6b (jidctred.c).
520 *
521 * NOTE: jpeg-8 has an improved implementation of 2x2 inverse-DCT, which
522 *       requires much less arithmetic operations and hence should be faster.
523 *       The primary purpose of this particular NEON optimized function is
524 *       bit exact compatibility with jpeg-6b.
525 */
526
527.balign 8
528jsimd_idct_2x2_neon_consts:
529    .short     -FIX_0_720959822    /* d0[0] */
530    .short     FIX_0_850430095     /* d0[1] */
531    .short     -FIX_1_272758580    /* d0[2] */
532    .short     FIX_3_624509785     /* d0[3] */
533
534.macro idct_helper x4, x6, x10, x12, x16, shift, y26, y27
535    vshll.s16  q14,  \x4,  #15
536    vmull.s16  q13,  \x6,  d0[3]
537    vmlal.s16  q13,  \x10, d0[2]
538    vmlal.s16  q13,  \x12, d0[1]
539    vmlal.s16  q13,  \x16, d0[0]
540
541    vadd.s32   q10,  q14,  q13
542    vsub.s32   q14,  q14,  q13
543
544.if \shift > 16
545    vrshr.s32  q10,  q10,  #\shift
546    vrshr.s32  q14,  q14,  #\shift
547    vmovn.s32  \y26, q10
548    vmovn.s32  \y27, q14
549.else
550    vrshrn.s32 \y26, q10,  #\shift
551    vrshrn.s32 \y27, q14,  #\shift
552.endif
553
554.endm
555
556asm_function jsimd_idct_2x2_neon
557
558    DCT_TABLE       .req r0
559    COEF_BLOCK      .req r1
560    OUTPUT_BUF      .req r2
561    OUTPUT_COL      .req r3
562    TMP1            .req r0
563    TMP2            .req ip
564
565    vpush           {d8-d15}
566
567    /* Load constants */
568    adr             TMP2, jsimd_idct_2x2_neon_consts
569    vld1.16         {d0}, [TMP2, :64]
570
571    /* Load all COEF_BLOCK into NEON registers with the following allocation:
572     *       0 1 2 3 | 4 5 6 7
573     *      ---------+--------
574     *   0 | d4      | d5
575     *   1 | d6      | d7
576     *   2 | -       | -
577     *   3 | d10     | d11
578     *   4 | -       | -
579     *   5 | d12     | d13
580     *   6 | -       | -
581     *   7 | d16     | d17
582     */
583    vld1.16         {d4, d5, d6, d7}, [COEF_BLOCK, :128]!
584    add             COEF_BLOCK, COEF_BLOCK, #16
585    vld1.16         {d10, d11}, [COEF_BLOCK, :128]!
586    add             COEF_BLOCK, COEF_BLOCK, #16
587    vld1.16         {d12, d13}, [COEF_BLOCK, :128]!
588    add             COEF_BLOCK, COEF_BLOCK, #16
589    vld1.16         {d16, d17}, [COEF_BLOCK, :128]!
590    /* Dequantize */
591    vld1.16         {d18, d19, d20, d21}, [DCT_TABLE, :128]!
592    vmul.s16        q2, q2, q9
593    vmul.s16        q3, q3, q10
594    add             DCT_TABLE, DCT_TABLE, #16
595    vld1.16         {d24, d25}, [DCT_TABLE, :128]!
596    vmul.s16        q5, q5, q12
597    add             DCT_TABLE, DCT_TABLE, #16
598    vld1.16         {d26, d27}, [DCT_TABLE, :128]!
599    vmul.s16        q6, q6, q13
600    add             DCT_TABLE, DCT_TABLE, #16
601    vld1.16         {d30, d31}, [DCT_TABLE, :128]!
602    vmul.s16        q8, q8, q15
603
604    /* Pass 1 */
605#if 0
606    idct_helper     d4, d6, d10, d12, d16, 13, d4, d6
607    transpose_4x4   d4, d6, d8,  d10
608    idct_helper     d5, d7, d11, d13, d17, 13, d5, d7
609    transpose_4x4   d5, d7, d9,  d11
610#else
611    vmull.s16       q13, d6,  d0[3]
612    vmlal.s16       q13, d10, d0[2]
613    vmlal.s16       q13, d12, d0[1]
614    vmlal.s16       q13, d16, d0[0]
615    vmull.s16       q12, d7,  d0[3]
616    vmlal.s16       q12, d11, d0[2]
617    vmlal.s16       q12, d13, d0[1]
618    vmlal.s16       q12, d17, d0[0]
619    vshll.s16       q14, d4,  #15
620    vshll.s16       q15, d5,  #15
621    vadd.s32        q10, q14, q13
622    vsub.s32        q14, q14, q13
623    vrshrn.s32      d4,  q10, #13
624    vrshrn.s32      d6,  q14, #13
625    vadd.s32        q10, q15, q12
626    vsub.s32        q14, q15, q12
627    vrshrn.s32      d5,  q10, #13
628    vrshrn.s32      d7,  q14, #13
629    vtrn.16         q2,  q3
630    vtrn.32         q3,  q5
631#endif
632
633    /* Pass 2 */
634    idct_helper     d4, d6, d10, d7, d11, 20, d26, d27
635
636    /* Range limit */
637    vmov.u16        q15, #0x80
638    vadd.s16        q13, q13, q15
639    vqmovun.s16     d26, q13
640    vqmovun.s16     d27, q13
641
642    /* Store results to the output buffer */
643    ldmia           OUTPUT_BUF, {TMP1, TMP2}
644    add             TMP1, TMP1, OUTPUT_COL
645    add             TMP2, TMP2, OUTPUT_COL
646
647    vst1.8          {d26[0]}, [TMP1]!
648    vst1.8          {d27[4]}, [TMP1]!
649    vst1.8          {d26[1]}, [TMP2]!
650    vst1.8          {d27[5]}, [TMP2]!
651
652    vpop            {d8-d15}
653    bx              lr
654
655    .unreq          DCT_TABLE
656    .unreq          COEF_BLOCK
657    .unreq          OUTPUT_BUF
658    .unreq          OUTPUT_COL
659    .unreq          TMP1
660    .unreq          TMP2
661.endfunc
662
663.purgem idct_helper
664
665/*****************************************************************************/
666
667/*
668 * jsimd_ycc_extrgb_convert_neon
669 * jsimd_ycc_extbgr_convert_neon
670 * jsimd_ycc_extrgbx_convert_neon
671 * jsimd_ycc_extbgrx_convert_neon
672 * jsimd_ycc_extxbgr_convert_neon
673 * jsimd_ycc_extxrgb_convert_neon
674 *
675 * Colorspace conversion YCbCr -> RGB
676 */
677
678
679.macro do_load size
680    .if \size == 8
681        vld1.8  {d4}, [U]!
682        vld1.8  {d5}, [V]!
683        vld1.8  {d0}, [Y]!
684        pld     [Y, #64]
685        pld     [U, #64]
686        pld     [V, #64]
687    .elseif \size == 4
688        vld1.8  {d4[0]}, [U]!
689        vld1.8  {d4[1]}, [U]!
690        vld1.8  {d4[2]}, [U]!
691        vld1.8  {d4[3]}, [U]!
692        vld1.8  {d5[0]}, [V]!
693        vld1.8  {d5[1]}, [V]!
694        vld1.8  {d5[2]}, [V]!
695        vld1.8  {d5[3]}, [V]!
696        vld1.8  {d0[0]}, [Y]!
697        vld1.8  {d0[1]}, [Y]!
698        vld1.8  {d0[2]}, [Y]!
699        vld1.8  {d0[3]}, [Y]!
700    .elseif \size == 2
701        vld1.8  {d4[4]}, [U]!
702        vld1.8  {d4[5]}, [U]!
703        vld1.8  {d5[4]}, [V]!
704        vld1.8  {d5[5]}, [V]!
705        vld1.8  {d0[4]}, [Y]!
706        vld1.8  {d0[5]}, [Y]!
707    .elseif \size == 1
708        vld1.8  {d4[6]}, [U]!
709        vld1.8  {d5[6]}, [V]!
710        vld1.8  {d0[6]}, [Y]!
711    .else
712        .error unsupported macroblock size
713    .endif
714.endm
715
716.macro do_store bpp, size
717    .if \bpp == 24
718        .if \size == 8
719            vst3.8  {d10, d11, d12}, [RGB]!
720        .elseif \size == 4
721            vst3.8  {d10[0], d11[0], d12[0]}, [RGB]!
722            vst3.8  {d10[1], d11[1], d12[1]}, [RGB]!
723            vst3.8  {d10[2], d11[2], d12[2]}, [RGB]!
724            vst3.8  {d10[3], d11[3], d12[3]}, [RGB]!
725        .elseif \size == 2
726            vst3.8  {d10[4], d11[4], d12[4]}, [RGB]!
727            vst3.8  {d10[5], d11[5], d12[5]}, [RGB]!
728        .elseif \size == 1
729            vst3.8  {d10[6], d11[6], d12[6]}, [RGB]!
730        .else
731            .error unsupported macroblock size
732        .endif
733    .elseif \bpp == 32
734        .if \size == 8
735            vst4.8  {d10, d11, d12, d13}, [RGB]!
736        .elseif \size == 4
737            vst4.8  {d10[0], d11[0], d12[0], d13[0]}, [RGB]!
738            vst4.8  {d10[1], d11[1], d12[1], d13[1]}, [RGB]!
739            vst4.8  {d10[2], d11[2], d12[2], d13[2]}, [RGB]!
740            vst4.8  {d10[3], d11[3], d12[3], d13[3]}, [RGB]!
741        .elseif \size == 2
742            vst4.8  {d10[4], d11[4], d12[4], d13[4]}, [RGB]!
743            vst4.8  {d10[5], d11[5], d12[5], d13[5]}, [RGB]!
744        .elseif \size == 1
745            vst4.8  {d10[6], d11[6], d12[6], d13[6]}, [RGB]!
746        .else
747            .error unsupported macroblock size
748        .endif
749    .else
750        .error unsupported bpp
751    .endif
752.endm
753
754.macro generate_jsimd_ycc_rgb_convert_neon colorid, bpp, r_offs, g_offs, b_offs
755
756.macro do_yuv_to_rgb
757    vaddw.u8        q3, q1, d4     /* q3 = u - 128 */
758    vaddw.u8        q4, q1, d5     /* q2 = v - 128 */
759    vmull.s16       q10, d6, d1[1] /* multiply by -11277 */
760    vmlal.s16       q10, d8, d1[2] /* multiply by -23401 */
761    vmull.s16       q11, d7, d1[1] /* multiply by -11277 */
762    vmlal.s16       q11, d9, d1[2] /* multiply by -23401 */
763    vmull.s16       q12, d8, d1[0] /* multiply by 22971 */
764    vmull.s16       q13, d9, d1[0] /* multiply by 22971 */
765    vmull.s16       q14, d6, d1[3] /* multiply by 29033 */
766    vmull.s16       q15, d7, d1[3] /* multiply by 29033 */
767    vrshrn.s32      d20, q10, #15
768    vrshrn.s32      d21, q11, #15
769    vrshrn.s32      d24, q12, #14
770    vrshrn.s32      d25, q13, #14
771    vrshrn.s32      d28, q14, #14
772    vrshrn.s32      d29, q15, #14
773    vaddw.u8        q10, q10, d0
774    vaddw.u8        q12, q12, d0
775    vaddw.u8        q14, q14, d0
776    vqmovun.s16     d1\g_offs, q10
777    vqmovun.s16     d1\r_offs, q12
778    vqmovun.s16     d1\b_offs, q14
779.endm
780
781/* Apple gas crashes on adrl, work around that by using adr.
782 * But this requires a copy of these constants for each function.
783 */
784
785.balign 16
786jsimd_ycc_\colorid\()_neon_consts:
787    .short          0,      0,     0,      0
788    .short          22971, -11277, -23401, 29033
789    .short          -128,  -128,   -128,   -128
790    .short          -128,  -128,   -128,   -128
791
792asm_function jsimd_ycc_\colorid\()_convert_neon
793    OUTPUT_WIDTH    .req r0
794    INPUT_BUF       .req r1
795    INPUT_ROW       .req r2
796    OUTPUT_BUF      .req r3
797    NUM_ROWS        .req r4
798
799    INPUT_BUF0      .req r5
800    INPUT_BUF1      .req r6
801    INPUT_BUF2      .req INPUT_BUF
802
803    RGB             .req r7
804    Y               .req r8
805    U               .req r9
806    V               .req r10
807    N               .req ip
808
809    /* Load constants to d1, d2, d3 (d0 is just used for padding) */
810    adr             ip, jsimd_ycc_\colorid\()_neon_consts
811    vld1.16         {d0, d1, d2, d3}, [ip, :128]
812
813    /* Save ARM registers and handle input arguments */
814    push            {r4, r5, r6, r7, r8, r9, r10, lr}
815    ldr             NUM_ROWS, [sp, #(4 * 8)]
816    ldr             INPUT_BUF0, [INPUT_BUF]
817    ldr             INPUT_BUF1, [INPUT_BUF, #4]
818    ldr             INPUT_BUF2, [INPUT_BUF, #8]
819    .unreq          INPUT_BUF
820
821    /* Save NEON registers */
822    vpush           {d8-d15}
823
824    /* Initially set d10, d11, d12, d13 to 0xFF */
825    vmov.u8         q5, #255
826    vmov.u8         q6, #255
827
828    /* Outer loop over scanlines */
829    cmp             NUM_ROWS, #1
830    blt             9f
8310:
832    ldr             Y, [INPUT_BUF0, INPUT_ROW, lsl #2]
833    ldr             U, [INPUT_BUF1, INPUT_ROW, lsl #2]
834    mov             N, OUTPUT_WIDTH
835    ldr             V, [INPUT_BUF2, INPUT_ROW, lsl #2]
836    add             INPUT_ROW, INPUT_ROW, #1
837    ldr             RGB, [OUTPUT_BUF], #4
838
839    /* Inner loop over pixels */
840    subs            N, N, #8
841    blt             2f
8421:
843    do_load         8
844    do_yuv_to_rgb
845    do_store        \bpp, 8
846    subs            N, N, #8
847    bge             1b
848    tst             N, #7
849    beq             8f
8502:
851    tst             N, #4
852    beq             3f
853    do_load         4
8543:
855    tst             N, #2
856    beq             4f
857    do_load         2
8584:
859    tst             N, #1
860    beq             5f
861    do_load         1
8625:
863    do_yuv_to_rgb
864    tst             N, #4
865    beq             6f
866    do_store        \bpp, 4
8676:
868    tst             N, #2
869    beq             7f
870    do_store        \bpp, 2
8717:
872    tst             N, #1
873    beq             8f
874    do_store        \bpp, 1
8758:
876    subs            NUM_ROWS, NUM_ROWS, #1
877    bgt             0b
8789:
879    /* Restore all registers and return */
880    vpop            {d8-d15}
881    pop             {r4, r5, r6, r7, r8, r9, r10, pc}
882
883    .unreq          OUTPUT_WIDTH
884    .unreq          INPUT_ROW
885    .unreq          OUTPUT_BUF
886    .unreq          NUM_ROWS
887    .unreq          INPUT_BUF0
888    .unreq          INPUT_BUF1
889    .unreq          INPUT_BUF2
890    .unreq          RGB
891    .unreq          Y
892    .unreq          U
893    .unreq          V
894    .unreq          N
895.endfunc
896
897.purgem do_yuv_to_rgb
898
899.endm
900
901/*--------------------------------- id ----- bpp R  G  B */
902generate_jsimd_ycc_rgb_convert_neon extrgb,  24, 0, 1, 2
903generate_jsimd_ycc_rgb_convert_neon extbgr,  24, 2, 1, 0
904generate_jsimd_ycc_rgb_convert_neon extrgbx, 32, 0, 1, 2
905generate_jsimd_ycc_rgb_convert_neon extbgrx, 32, 2, 1, 0
906generate_jsimd_ycc_rgb_convert_neon extxbgr, 32, 3, 2, 1
907generate_jsimd_ycc_rgb_convert_neon extxrgb, 32, 1, 2, 3
908
909.purgem do_load
910.purgem do_store
911
912/*****************************************************************************/
913
914/*
915 * jsimd_extrgb_ycc_convert_neon
916 * jsimd_extbgr_ycc_convert_neon
917 * jsimd_extrgbx_ycc_convert_neon
918 * jsimd_extbgrx_ycc_convert_neon
919 * jsimd_extxbgr_ycc_convert_neon
920 * jsimd_extxrgb_ycc_convert_neon
921 *
922 * Colorspace conversion RGB -> YCbCr
923 */
924
925.macro do_store size
926    .if \size == 8
927        vst1.8  {d20}, [Y]!
928        vst1.8  {d21}, [U]!
929        vst1.8  {d22}, [V]!
930    .elseif \size == 4
931        vst1.8  {d20[0]}, [Y]!
932        vst1.8  {d20[1]}, [Y]!
933        vst1.8  {d20[2]}, [Y]!
934        vst1.8  {d20[3]}, [Y]!
935        vst1.8  {d21[0]}, [U]!
936        vst1.8  {d21[1]}, [U]!
937        vst1.8  {d21[2]}, [U]!
938        vst1.8  {d21[3]}, [U]!
939        vst1.8  {d22[0]}, [V]!
940        vst1.8  {d22[1]}, [V]!
941        vst1.8  {d22[2]}, [V]!
942        vst1.8  {d22[3]}, [V]!
943    .elseif \size == 2
944        vst1.8  {d20[4]}, [Y]!
945        vst1.8  {d20[5]}, [Y]!
946        vst1.8  {d21[4]}, [U]!
947        vst1.8  {d21[5]}, [U]!
948        vst1.8  {d22[4]}, [V]!
949        vst1.8  {d22[5]}, [V]!
950    .elseif \size == 1
951        vst1.8  {d20[6]}, [Y]!
952        vst1.8  {d21[6]}, [U]!
953        vst1.8  {d22[6]}, [V]!
954    .else
955        .error unsupported macroblock size
956    .endif
957.endm
958
959.macro do_load bpp, size
960    .if \bpp == 24
961        .if \size == 8
962            vld3.8  {d10, d11, d12}, [RGB]!
963            pld     [RGB, #128]
964        .elseif \size == 4
965            vld3.8  {d10[0], d11[0], d12[0]}, [RGB]!
966            vld3.8  {d10[1], d11[1], d12[1]}, [RGB]!
967            vld3.8  {d10[2], d11[2], d12[2]}, [RGB]!
968            vld3.8  {d10[3], d11[3], d12[3]}, [RGB]!
969        .elseif \size == 2
970            vld3.8  {d10[4], d11[4], d12[4]}, [RGB]!
971            vld3.8  {d10[5], d11[5], d12[5]}, [RGB]!
972        .elseif \size == 1
973            vld3.8  {d10[6], d11[6], d12[6]}, [RGB]!
974        .else
975            .error unsupported macroblock size
976        .endif
977    .elseif \bpp == 32
978        .if \size == 8
979            vld4.8  {d10, d11, d12, d13}, [RGB]!
980            pld     [RGB, #128]
981        .elseif \size == 4
982            vld4.8  {d10[0], d11[0], d12[0], d13[0]}, [RGB]!
983            vld4.8  {d10[1], d11[1], d12[1], d13[1]}, [RGB]!
984            vld4.8  {d10[2], d11[2], d12[2], d13[2]}, [RGB]!
985            vld4.8  {d10[3], d11[3], d12[3], d13[3]}, [RGB]!
986        .elseif \size == 2
987            vld4.8  {d10[4], d11[4], d12[4], d13[4]}, [RGB]!
988            vld4.8  {d10[5], d11[5], d12[5], d13[5]}, [RGB]!
989        .elseif \size == 1
990            vld4.8  {d10[6], d11[6], d12[6], d13[6]}, [RGB]!
991        .else
992            .error unsupported macroblock size
993        .endif
994    .else
995        .error unsupported bpp
996    .endif
997.endm
998
999.macro generate_jsimd_rgb_ycc_convert_neon colorid, bpp, r_offs, g_offs, b_offs
1000
1001/*
1002 * 2 stage pipelined RGB->YCbCr conversion
1003 */
1004
1005.macro do_rgb_to_yuv_stage1
1006    vmovl.u8    q2, d1\r_offs /* r = { d4, d5 } */
1007    vmovl.u8    q3, d1\g_offs /* g = { d6, d7 } */
1008    vmovl.u8    q4, d1\b_offs /* b = { d8, d9 } */
1009    vmull.u16   q7, d4, d0[0]
1010    vmlal.u16   q7, d6, d0[1]
1011    vmlal.u16   q7, d8, d0[2]
1012    vmull.u16   q8, d5, d0[0]
1013    vmlal.u16   q8, d7, d0[1]
1014    vmlal.u16   q8, d9, d0[2]
1015    vrev64.32   q9,  q1
1016    vrev64.32   q13, q1
1017    vmlsl.u16   q9,  d4, d0[3]
1018    vmlsl.u16   q9,  d6, d1[0]
1019    vmlal.u16   q9,  d8, d1[1]
1020    vmlsl.u16   q13, d5, d0[3]
1021    vmlsl.u16   q13, d7, d1[0]
1022    vmlal.u16   q13, d9, d1[1]
1023    vrev64.32   q14, q1
1024    vrev64.32   q15, q1
1025    vmlal.u16   q14, d4, d1[1]
1026    vmlsl.u16   q14, d6, d1[2]
1027    vmlsl.u16   q14, d8, d1[3]
1028    vmlal.u16   q15, d5, d1[1]
1029    vmlsl.u16   q15, d7, d1[2]
1030    vmlsl.u16   q15, d9, d1[3]
1031.endm
1032
1033.macro do_rgb_to_yuv_stage2
1034    vrshrn.u32  d20, q7,  #16
1035    vrshrn.u32  d21, q8,  #16
1036    vshrn.u32   d22, q9,  #16
1037    vshrn.u32   d23, q13, #16
1038    vshrn.u32   d24, q14, #16
1039    vshrn.u32   d25, q15, #16
1040    vmovn.u16   d20, q10      /* d20 = y */
1041    vmovn.u16   d21, q11      /* d21 = u */
1042    vmovn.u16   d22, q12      /* d22 = v */
1043.endm
1044
1045.macro do_rgb_to_yuv
1046    do_rgb_to_yuv_stage1
1047    do_rgb_to_yuv_stage2
1048.endm
1049
1050.macro do_rgb_to_yuv_stage2_store_load_stage1
1051      vrshrn.u32  d20, q7,  #16
1052      vrshrn.u32  d21, q8,  #16
1053      vshrn.u32   d22, q9,  #16
1054    vrev64.32   q9,  q1
1055      vshrn.u32   d23, q13, #16
1056    vrev64.32   q13, q1
1057      vshrn.u32   d24, q14, #16
1058      vshrn.u32   d25, q15, #16
1059    do_load     \bpp, 8
1060      vmovn.u16   d20, q10      /* d20 = y */
1061    vmovl.u8    q2, d1\r_offs   /* r = { d4, d5 } */
1062      vmovn.u16   d21, q11      /* d21 = u */
1063    vmovl.u8    q3, d1\g_offs   /* g = { d6, d7 } */
1064      vmovn.u16   d22, q12      /* d22 = v */
1065    vmovl.u8    q4, d1\b_offs   /* b = { d8, d9 } */
1066    vmull.u16   q7, d4, d0[0]
1067    vmlal.u16   q7, d6, d0[1]
1068    vmlal.u16   q7, d8, d0[2]
1069      vst1.8      {d20}, [Y]!
1070    vmull.u16   q8, d5, d0[0]
1071    vmlal.u16   q8, d7, d0[1]
1072    vmlal.u16   q8, d9, d0[2]
1073    vmlsl.u16   q9,  d4, d0[3]
1074    vmlsl.u16   q9,  d6, d1[0]
1075    vmlal.u16   q9,  d8, d1[1]
1076      vst1.8      {d21}, [U]!
1077    vmlsl.u16   q13, d5, d0[3]
1078    vmlsl.u16   q13, d7, d1[0]
1079    vmlal.u16   q13, d9, d1[1]
1080    vrev64.32   q14, q1
1081    vrev64.32   q15, q1
1082    vmlal.u16   q14, d4, d1[1]
1083    vmlsl.u16   q14, d6, d1[2]
1084    vmlsl.u16   q14, d8, d1[3]
1085      vst1.8      {d22}, [V]!
1086    vmlal.u16   q15, d5, d1[1]
1087    vmlsl.u16   q15, d7, d1[2]
1088    vmlsl.u16   q15, d9, d1[3]
1089.endm
1090
1091.balign 16
1092jsimd_\colorid\()_ycc_neon_consts:
1093    .short          19595, 38470, 7471,  11059
1094    .short          21709, 32768, 27439, 5329
1095    .short          32767, 128,   32767, 128
1096    .short          32767, 128,   32767, 128
1097
1098asm_function jsimd_\colorid\()_ycc_convert_neon
1099    OUTPUT_WIDTH    .req r0
1100    INPUT_BUF       .req r1
1101    OUTPUT_BUF      .req r2
1102    OUTPUT_ROW      .req r3
1103    NUM_ROWS        .req r4
1104
1105    OUTPUT_BUF0     .req r5
1106    OUTPUT_BUF1     .req r6
1107    OUTPUT_BUF2     .req OUTPUT_BUF
1108
1109    RGB             .req r7
1110    Y               .req r8
1111    U               .req r9
1112    V               .req r10
1113    N               .req ip
1114
1115    /* Load constants to d0, d1, d2, d3 */
1116    adr             ip, jsimd_\colorid\()_ycc_neon_consts
1117    vld1.16         {d0, d1, d2, d3}, [ip, :128]
1118
1119    /* Save ARM registers and handle input arguments */
1120    push            {r4, r5, r6, r7, r8, r9, r10, lr}
1121    ldr             NUM_ROWS, [sp, #(4 * 8)]
1122    ldr             OUTPUT_BUF0, [OUTPUT_BUF]
1123    ldr             OUTPUT_BUF1, [OUTPUT_BUF, #4]
1124    ldr             OUTPUT_BUF2, [OUTPUT_BUF, #8]
1125    .unreq          OUTPUT_BUF
1126
1127    /* Save NEON registers */
1128    vpush           {d8-d15}
1129
1130    /* Outer loop over scanlines */
1131    cmp             NUM_ROWS, #1
1132    blt             9f
11330:
1134    ldr             Y, [OUTPUT_BUF0, OUTPUT_ROW, lsl #2]
1135    ldr             U, [OUTPUT_BUF1, OUTPUT_ROW, lsl #2]
1136    mov             N, OUTPUT_WIDTH
1137    ldr             V, [OUTPUT_BUF2, OUTPUT_ROW, lsl #2]
1138    add             OUTPUT_ROW, OUTPUT_ROW, #1
1139    ldr             RGB, [INPUT_BUF], #4
1140
1141    /* Inner loop over pixels */
1142    subs            N, N, #8
1143    blt             3f
1144    do_load         \bpp, 8
1145    do_rgb_to_yuv_stage1
1146    subs            N, N, #8
1147    blt             2f
11481:
1149    do_rgb_to_yuv_stage2_store_load_stage1
1150    subs            N, N, #8
1151    bge             1b
11522:
1153    do_rgb_to_yuv_stage2
1154    do_store        8
1155    tst             N, #7
1156    beq             8f
11573:
1158    tst             N, #4
1159    beq             3f
1160    do_load         \bpp, 4
11613:
1162    tst             N, #2
1163    beq             4f
1164    do_load         \bpp, 2
11654:
1166    tst             N, #1
1167    beq             5f
1168    do_load         \bpp, 1
11695:
1170    do_rgb_to_yuv
1171    tst             N, #4
1172    beq             6f
1173    do_store        4
11746:
1175    tst             N, #2
1176    beq             7f
1177    do_store        2
11787:
1179    tst             N, #1
1180    beq             8f
1181    do_store        1
11828:
1183    subs            NUM_ROWS, NUM_ROWS, #1
1184    bgt             0b
11859:
1186    /* Restore all registers and return */
1187    vpop            {d8-d15}
1188    pop             {r4, r5, r6, r7, r8, r9, r10, pc}
1189
1190    .unreq          OUTPUT_WIDTH
1191    .unreq          OUTPUT_ROW
1192    .unreq          INPUT_BUF
1193    .unreq          NUM_ROWS
1194    .unreq          OUTPUT_BUF0
1195    .unreq          OUTPUT_BUF1
1196    .unreq          OUTPUT_BUF2
1197    .unreq          RGB
1198    .unreq          Y
1199    .unreq          U
1200    .unreq          V
1201    .unreq          N
1202.endfunc
1203
1204.purgem do_rgb_to_yuv
1205.purgem do_rgb_to_yuv_stage1
1206.purgem do_rgb_to_yuv_stage2
1207.purgem do_rgb_to_yuv_stage2_store_load_stage1
1208
1209.endm
1210
1211/*--------------------------------- id ----- bpp R  G  B */
1212generate_jsimd_rgb_ycc_convert_neon extrgb,  24, 0, 1, 2
1213generate_jsimd_rgb_ycc_convert_neon extbgr,  24, 2, 1, 0
1214generate_jsimd_rgb_ycc_convert_neon extrgbx, 32, 0, 1, 2
1215generate_jsimd_rgb_ycc_convert_neon extbgrx, 32, 2, 1, 0
1216generate_jsimd_rgb_ycc_convert_neon extxbgr, 32, 3, 2, 1
1217generate_jsimd_rgb_ycc_convert_neon extxrgb, 32, 1, 2, 3
1218
1219.purgem do_load
1220.purgem do_store
1221
1222/*****************************************************************************/
1223
1224/*
1225 * Load data into workspace, applying unsigned->signed conversion
1226 *
1227 * TODO: can be combined with 'jsimd_fdct_ifast_neon' to get
1228 *       rid of VST1.16 instructions
1229 */
1230
1231asm_function jsimd_convsamp_neon
1232    SAMPLE_DATA     .req r0
1233    START_COL       .req r1
1234    WORKSPACE       .req r2
1235    TMP1            .req r3
1236    TMP2            .req r4
1237    TMP3            .req r5
1238    TMP4            .req ip
1239
1240    push            {r4, r5}
1241    vmov.u8         d0, #128
1242
1243    ldmia           SAMPLE_DATA!, {TMP1, TMP2, TMP3, TMP4}
1244    add             TMP1, TMP1, START_COL
1245    add             TMP2, TMP2, START_COL
1246    add             TMP3, TMP3, START_COL
1247    add             TMP4, TMP4, START_COL
1248    vld1.8          {d16}, [TMP1]
1249    vsubl.u8        q8, d16, d0
1250    vld1.8          {d18}, [TMP2]
1251    vsubl.u8        q9, d18, d0
1252    vld1.8          {d20}, [TMP3]
1253    vsubl.u8        q10, d20, d0
1254    vld1.8          {d22}, [TMP4]
1255    ldmia           SAMPLE_DATA!, {TMP1, TMP2, TMP3, TMP4}
1256    vsubl.u8        q11, d22, d0
1257    vst1.16         {d16, d17, d18, d19}, [WORKSPACE, :128]!
1258    add             TMP1, TMP1, START_COL
1259    add             TMP2, TMP2, START_COL
1260    vst1.16         {d20, d21, d22, d23}, [WORKSPACE, :128]!
1261    add             TMP3, TMP3, START_COL
1262    add             TMP4, TMP4, START_COL
1263    vld1.8          {d24}, [TMP1]
1264    vsubl.u8        q12, d24, d0
1265    vld1.8          {d26}, [TMP2]
1266    vsubl.u8        q13, d26, d0
1267    vld1.8          {d28}, [TMP3]
1268    vsubl.u8        q14, d28, d0
1269    vld1.8          {d30}, [TMP4]
1270    vsubl.u8        q15, d30, d0
1271    vst1.16         {d24, d25, d26, d27}, [WORKSPACE, :128]!
1272    vst1.16         {d28, d29, d30, d31}, [WORKSPACE, :128]!
1273    pop             {r4, r5}
1274    bx              lr
1275
1276    .unreq          SAMPLE_DATA
1277    .unreq          START_COL
1278    .unreq          WORKSPACE
1279    .unreq          TMP1
1280    .unreq          TMP2
1281    .unreq          TMP3
1282    .unreq          TMP4
1283.endfunc
1284
1285/*****************************************************************************/
1286
1287/*
1288 * jsimd_fdct_ifast_neon
1289 *
1290 * This function contains a fast, not so accurate integer implementation of
1291 * the forward DCT (Discrete Cosine Transform). It uses the same calculations
1292 * and produces exactly the same output as IJG's original 'jpeg_fdct_ifast'
1293 * function from jfdctfst.c
1294 *
1295 * TODO: can be combined with 'jsimd_convsamp_neon' to get
1296 *       rid of a bunch of VLD1.16 instructions
1297 */
1298
1299#define XFIX_0_382683433 d0[0]
1300#define XFIX_0_541196100 d0[1]
1301#define XFIX_0_707106781 d0[2]
1302#define XFIX_1_306562965 d0[3]
1303
1304.balign 16
1305jsimd_fdct_ifast_neon_consts:
1306    .short (98 * 128)              /* XFIX_0_382683433 */
1307    .short (139 * 128)             /* XFIX_0_541196100 */
1308    .short (181 * 128)             /* XFIX_0_707106781 */
1309    .short (334 * 128 - 256 * 128) /* XFIX_1_306562965 */
1310
1311asm_function jsimd_fdct_ifast_neon
1312
1313    DATA            .req r0
1314    TMP             .req ip
1315
1316    vpush           {d8-d15}
1317
1318    /* Load constants */
1319    adr             TMP, jsimd_fdct_ifast_neon_consts
1320    vld1.16         {d0}, [TMP, :64]
1321
1322    /* Load all DATA into NEON registers with the following allocation:
1323     *       0 1 2 3 | 4 5 6 7
1324     *      ---------+--------
1325     *   0 | d16     | d17    | q8
1326     *   1 | d18     | d19    | q9
1327     *   2 | d20     | d21    | q10
1328     *   3 | d22     | d23    | q11
1329     *   4 | d24     | d25    | q12
1330     *   5 | d26     | d27    | q13
1331     *   6 | d28     | d29    | q14
1332     *   7 | d30     | d31    | q15
1333     */
1334
1335    vld1.16         {d16, d17, d18, d19}, [DATA, :128]!
1336    vld1.16         {d20, d21, d22, d23}, [DATA, :128]!
1337    vld1.16         {d24, d25, d26, d27}, [DATA, :128]!
1338    vld1.16         {d28, d29, d30, d31}, [DATA, :128]
1339    sub             DATA, DATA, #(128 - 32)
1340
1341    mov             TMP, #2
13421:
1343    /* Transpose */
1344    vtrn.16         q12, q13
1345    vtrn.16         q10, q11
1346    vtrn.16         q8,  q9
1347    vtrn.16         q14, q15
1348    vtrn.32         q9,  q11
1349    vtrn.32         q13, q15
1350    vtrn.32         q8,  q10
1351    vtrn.32         q12, q14
1352    vswp            d30, d23
1353    vswp            d24, d17
1354    vswp            d26, d19
1355      /* 1-D FDCT */
1356      vadd.s16        q2,  q11, q12
1357    vswp            d28, d21
1358      vsub.s16        q12, q11, q12
1359      vsub.s16        q6,  q10, q13
1360      vadd.s16        q10, q10, q13
1361      vsub.s16        q7,  q9,  q14
1362      vadd.s16        q9,  q9,  q14
1363      vsub.s16        q1,  q8,  q15
1364      vadd.s16        q8,  q8,  q15
1365      vsub.s16        q4,  q9,  q10
1366      vsub.s16        q5,  q8,  q2
1367      vadd.s16        q3,  q9,  q10
1368      vadd.s16        q4,  q4,  q5
1369      vadd.s16        q2,  q8,  q2
1370      vqdmulh.s16     q4,  q4,  XFIX_0_707106781
1371      vadd.s16        q11, q12, q6
1372      vadd.s16        q8,  q2,  q3
1373      vsub.s16        q12, q2,  q3
1374      vadd.s16        q3,  q6,  q7
1375      vadd.s16        q7,  q7,  q1
1376      vqdmulh.s16     q3,  q3,  XFIX_0_707106781
1377      vsub.s16        q6,  q11, q7
1378      vadd.s16        q10, q5,  q4
1379      vqdmulh.s16     q6,  q6,  XFIX_0_382683433
1380      vsub.s16        q14, q5,  q4
1381      vqdmulh.s16     q11, q11, XFIX_0_541196100
1382      vqdmulh.s16     q5,  q7,  XFIX_1_306562965
1383      vadd.s16        q4,  q1,  q3
1384      vsub.s16        q3,  q1,  q3
1385      vadd.s16        q7,  q7,  q6
1386      vadd.s16        q11, q11, q6
1387      vadd.s16        q7,  q7,  q5
1388      vadd.s16        q13, q3,  q11
1389      vsub.s16        q11, q3,  q11
1390      vadd.s16        q9,  q4,  q7
1391      vsub.s16        q15, q4,  q7
1392    subs            TMP, TMP, #1
1393    bne             1b
1394
1395    /* store results */
1396    vst1.16         {d16, d17, d18, d19}, [DATA, :128]!
1397    vst1.16         {d20, d21, d22, d23}, [DATA, :128]!
1398    vst1.16         {d24, d25, d26, d27}, [DATA, :128]!
1399    vst1.16         {d28, d29, d30, d31}, [DATA, :128]
1400
1401    vpop            {d8-d15}
1402    bx              lr
1403
1404    .unreq          DATA
1405    .unreq          TMP
1406.endfunc
1407
1408/*****************************************************************************/
1409
1410/*
1411 * GLOBAL(void)
1412 * jsimd_quantize_neon (JCOEFPTR coef_block, DCTELEM * divisors,
1413 *                      DCTELEM * workspace);
1414 *
1415 * Note: the code uses 2 stage pipelining in order to improve instructions
1416 *       scheduling and eliminate stalls (this provides ~15% better
1417 *       performance for this function on both ARM Cortex-A8 and
1418 *       ARM Cortex-A9 when compared to the non-pipelined variant).
1419 *       The instructions which belong to the second stage use different
1420 *       indentation for better readiability.
1421 */
1422asm_function jsimd_quantize_neon
1423
1424    COEF_BLOCK      .req r0
1425    DIVISORS        .req r1
1426    WORKSPACE       .req r2
1427
1428    RECIPROCAL      .req DIVISORS
1429    CORRECTION      .req r3
1430    SHIFT           .req ip
1431    LOOP_COUNT      .req r4
1432
1433    vld1.16         {d0, d1, d2, d3}, [WORKSPACE, :128]!
1434    vabs.s16        q12, q0
1435    add             CORRECTION, DIVISORS, #(64 * 2)
1436    add             SHIFT, DIVISORS, #(64 * 6)
1437    vld1.16         {d20, d21, d22, d23}, [CORRECTION, :128]!
1438    vabs.s16        q13, q1
1439    vld1.16         {d16, d17, d18, d19}, [RECIPROCAL, :128]!
1440    vadd.u16        q12, q12, q10 /* add correction */
1441    vadd.u16        q13, q13, q11
1442    vmull.u16       q10, d24, d16 /* multiply by reciprocal */
1443    vmull.u16       q11, d25, d17
1444    vmull.u16       q8,  d26, d18
1445    vmull.u16       q9,  d27, d19
1446    vld1.16         {d24, d25, d26, d27}, [SHIFT, :128]!
1447    vshrn.u32       d20, q10, #16
1448    vshrn.u32       d21, q11, #16
1449    vshrn.u32       d22, q8,  #16
1450    vshrn.u32       d23, q9,  #16
1451    vneg.s16        q12, q12
1452    vneg.s16        q13, q13
1453    vshr.s16        q2,  q0,  #15 /* extract sign */
1454    vshr.s16        q3,  q1,  #15
1455    vshl.u16        q14, q10, q12 /* shift */
1456    vshl.u16        q15, q11, q13
1457
1458    push            {r4, r5}
1459    mov             LOOP_COUNT, #3
14601:
1461    vld1.16         {d0, d1, d2, d3}, [WORKSPACE, :128]!
1462      veor.u16        q14, q14, q2  /* restore sign */
1463    vabs.s16        q12, q0
1464    vld1.16         {d20, d21, d22, d23}, [CORRECTION, :128]!
1465    vabs.s16        q13, q1
1466      veor.u16        q15, q15, q3
1467    vld1.16         {d16, d17, d18, d19}, [RECIPROCAL, :128]!
1468    vadd.u16        q12, q12, q10 /* add correction */
1469    vadd.u16        q13, q13, q11
1470    vmull.u16       q10, d24, d16 /* multiply by reciprocal */
1471    vmull.u16       q11, d25, d17
1472    vmull.u16       q8,  d26, d18
1473    vmull.u16       q9,  d27, d19
1474      vsub.u16        q14, q14, q2
1475    vld1.16         {d24, d25, d26, d27}, [SHIFT, :128]!
1476      vsub.u16        q15, q15, q3
1477    vshrn.u32       d20, q10, #16
1478    vshrn.u32       d21, q11, #16
1479      vst1.16         {d28, d29, d30, d31}, [COEF_BLOCK, :128]!
1480    vshrn.u32       d22, q8,  #16
1481    vshrn.u32       d23, q9,  #16
1482    vneg.s16        q12, q12
1483    vneg.s16        q13, q13
1484    vshr.s16        q2,  q0,  #15 /* extract sign */
1485    vshr.s16        q3,  q1,  #15
1486    vshl.u16        q14, q10, q12 /* shift */
1487    vshl.u16        q15, q11, q13
1488    subs            LOOP_COUNT, LOOP_COUNT, #1
1489    bne             1b
1490    pop             {r4, r5}
1491
1492      veor.u16        q14, q14, q2  /* restore sign */
1493      veor.u16        q15, q15, q3
1494      vsub.u16        q14, q14, q2
1495      vsub.u16        q15, q15, q3
1496      vst1.16         {d28, d29, d30, d31}, [COEF_BLOCK, :128]!
1497
1498    bx              lr /* return */
1499
1500    .unreq          COEF_BLOCK
1501    .unreq          DIVISORS
1502    .unreq          WORKSPACE
1503    .unreq          RECIPROCAL
1504    .unreq          CORRECTION
1505    .unreq          SHIFT
1506    .unreq          LOOP_COUNT
1507.endfunc
1508