Improve performance of ARM NEON IFAST iDCT
git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@686 632fc199-4ca6-4c93-a231-07263d6284db
diff --git a/simd/jsimd_arm_neon.S b/simd/jsimd_arm_neon.S
index 45cd0e5..c942352 100644
--- a/simd/jsimd_arm_neon.S
+++ b/simd/jsimd_arm_neon.S
@@ -69,10 +69,15 @@
*
* This function contains a fast, not so accurate integer implementation of
* the inverse DCT (Discrete Cosine Transform). It uses the same calculations
- * and produces exactly the same output as IJG's original 'jpeg_idct_fast'
+ * and produces exactly the same output as IJG's original 'jpeg_idct_ifast'
* function from jidctfst.c
*
- * TODO: a bit better instructions scheduling is needed.
+ * Normally 1-D AAN DCT needs 5 multiplications and 29 additions.
+ * But in ARM NEON case some extra additions are required because VQDMULH
+ * instruction can't handle the constants larger than 1. So the expressions
+ * like "x * 1.082392200" have to be converted to "x * 0.082392200 + x",
+ * which introduces an extra addition. Overall, there are 6 extra additions
+ * per 1-D IDCT pass, totalling to 5 VQDMULH and 35 VADD/VSUB instructions.
*/
#define XFIX_1_082392200 d0[0]
@@ -87,166 +92,208 @@
.short (473 * 128 - 256 * 128) /* XFIX_1_847759065 */
.short (669 * 128 - 512 * 128) /* XFIX_2_613125930 */
-/* 1-D IDCT helper macro */
-
-.macro idct_helper x0, x1, x2, x3, x4, x5, x6, x7, \
- t10, t11, t12, t13, t14
-
- vsub.s16 \t10, \x0, \x4
- vadd.s16 \x4, \x0, \x4
- vswp.s16 \t10, \x0
- vsub.s16 \t11, \x2, \x6
- vadd.s16 \x6, \x2, \x6
- vswp.s16 \t11, \x2
- vsub.s16 \t10, \x3, \x5
- vadd.s16 \x5, \x3, \x5
- vswp.s16 \t10, \x3
- vsub.s16 \t11, \x1, \x7
- vadd.s16 \x7, \x1, \x7
- vswp.s16 \t11, \x1
-
- vqdmulh.s16 \t13, \x2, d0[1]
- vadd.s16 \t12, \x3, \x3
- vadd.s16 \x2, \x2, \t13
- vqdmulh.s16 \t13, \x3, d0[3]
- vsub.s16 \t10, \x1, \x3
- vadd.s16 \t12, \t12, \t13
- vqdmulh.s16 \t13, \t10, d0[2]
- vsub.s16 \t11, \x7, \x5
- vadd.s16 \t10, \t10, \t13
- vqdmulh.s16 \t13, \t11, d0[1]
- vadd.s16 \t11, \t11, \t13
-
- vqdmulh.s16 \t13, \x1, d0[0]
- vsub.s16 \x2, \x6, \x2
- vsub.s16 \t14, \x0, \x2
- vadd.s16 \x2, \x0, \x2
- vadd.s16 \x0, \x4, \x6
- vsub.s16 \x4, \x4, \x6
- vadd.s16 \x1, \x1, \t13
- vadd.s16 \t13, \x7, \x5
- vsub.s16 \t12, \t13, \t12
- vsub.s16 \t12, \t12, \t10
- vadd.s16 \t11, \t12, \t11
- vsub.s16 \t10, \x1, \t10
- vadd.s16 \t10, \t10, \t11
-
- vsub.s16 \x7, \x0, \t13
- vadd.s16 \x0, \x0, \t13
- vadd.s16 \x6, \t14, \t12
- vsub.s16 \x1, \t14, \t12
- vsub.s16 \x5, \x2, \t11
- vadd.s16 \x2, \x2, \t11
- vsub.s16 \x3, \x4, \t10
- vadd.s16 \x4, \x4, \t10
-.endm
-
asm_function jsimd_idct_ifast_neon
DCT_TABLE .req r0
COEF_BLOCK .req r1
OUTPUT_BUF .req r2
OUTPUT_COL .req r3
- TMP .req ip
+ TMP1 .req r0
+ TMP2 .req r1
+ TMP3 .req r2
+ TMP4 .req ip
- vpush {d8-d15}
-
- /* Load constants */
- adr TMP, jsimd_idct_ifast_neon_consts
- vld1.16 {d0}, [TMP, :64]
-
- /* Load all COEF_BLOCK into NEON registers with the following allocation:
+ /* Load and dequantize coefficients into NEON registers
+ * with the following allocation:
* 0 1 2 3 | 4 5 6 7
* ---------+--------
- * 0 | d4 | d5
- * 1 | d6 | d7
- * 2 | d8 | d9
- * 3 | d10 | d11
- * 4 | d12 | d13
- * 5 | d14 | d15
- * 6 | d16 | d17
- * 7 | d18 | d19
+ * 0 | d16 | d17 ( q8 )
+ * 1 | d18 | d19 ( q9 )
+ * 2 | d20 | d21 ( q10 )
+ * 3 | d22 | d23 ( q11 )
+ * 4 | d24 | d25 ( q12 )
+ * 5 | d26 | d27 ( q13 )
+ * 6 | d28 | d29 ( q14 )
+ * 7 | d30 | d31 ( q15 )
*/
- vld1.16 {d4, d5, d6, d7}, [COEF_BLOCK]!
- vld1.16 {d8, d9, d10, d11}, [COEF_BLOCK]!
- vld1.16 {d12, d13, d14, d15}, [COEF_BLOCK]!
- vld1.16 {d16, d17, d18, d19}, [COEF_BLOCK]!
- /* Dequantize */
- vld1.16 {d20, d21, d22, d23}, [DCT_TABLE]!
- vmul.s16 q2, q2, q10
- vld1.16 {d24, d25, d26, d27}, [DCT_TABLE]!
- vmul.s16 q3, q3, q11
- vmul.s16 q4, q4, q12
- vld1.16 {d28, d29, d30, d31}, [DCT_TABLE]!
- vmul.s16 q5, q5, q13
- vmul.s16 q6, q6, q14
- vld1.16 {d20, d21, d22, d23}, [DCT_TABLE]!
- vmul.s16 q7, q7, q15
- vmul.s16 q8, q8, q10
- vmul.s16 q9, q9, q11
-
- /* Pass 1 */
- idct_helper q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, q13, q14
- /* Transpose */
- transpose_4x4 d4, d6, d8, d10
- transpose_4x4 d5, d7, d9, d11
- transpose_4x4 d12, d14, d16, d18
- transpose_4x4 d13, d15, d17, d19
- vswp d12, d5
- vswp d14, d7
- vswp d16, d9
- vswp d18, d11
-
- /* Pass 2 */
- idct_helper q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, q13, q14
- /* Transpose */
- transpose_4x4 d4, d6, d8, d10
- transpose_4x4 d5, d7, d9, d11
- transpose_4x4 d12, d14, d16, d18
- transpose_4x4 d13, d15, d17, d19
- vswp d12, d5
- vswp d14, d7
- vswp d16, d9
- vswp d18, d11
-
+ adr ip, jsimd_idct_ifast_neon_consts
+ vld1.16 {d16, d17, d18, d19}, [COEF_BLOCK, :128]!
+ vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]!
+ vld1.16 {d20, d21, d22, d23}, [COEF_BLOCK, :128]!
+ vmul.s16 q8, q8, q0
+ vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]!
+ vmul.s16 q9, q9, q1
+ vld1.16 {d24, d25, d26, d27}, [COEF_BLOCK, :128]!
+ vmul.s16 q10, q10, q2
+ vld1.16 {d0, d1, d2, d3}, [DCT_TABLE, :128]!
+ vmul.s16 q11, q11, q3
+ vld1.16 {d28, d29, d30, d31}, [COEF_BLOCK, :128]
+ vmul.s16 q12, q12, q0
+ vld1.16 {d4, d5, d6, d7}, [DCT_TABLE, :128]!
+ vmul.s16 q14, q14, q2
+ vmul.s16 q13, q13, q1
+ vld1.16 {d0}, [ip, :64] /* load constants */
+ vmul.s16 q15, q15, q3
+ vpush {d8-d13} /* save NEON registers */
+ /* 1-D IDCT, pass 1 */
+ vsub.s16 q2, q10, q14
+ vadd.s16 q14, q10, q14
+ vsub.s16 q1, q11, q13
+ vadd.s16 q13, q11, q13
+ vsub.s16 q5, q9, q15
+ vadd.s16 q15, q9, q15
+ vqdmulh.s16 q4, q2, XFIX_1_414213562
+ vqdmulh.s16 q6, q1, XFIX_2_613125930
+ vadd.s16 q3, q1, q1
+ vsub.s16 q1, q5, q1
+ vadd.s16 q10, q2, q4
+ vqdmulh.s16 q4, q1, XFIX_1_847759065
+ vsub.s16 q2, q15, q13
+ vadd.s16 q3, q3, q6
+ vqdmulh.s16 q6, q2, XFIX_1_414213562
+ vadd.s16 q1, q1, q4
+ vqdmulh.s16 q4, q5, XFIX_1_082392200
+ vsub.s16 q10, q10, q14
+ vadd.s16 q2, q2, q6
+ vsub.s16 q6, q8, q12
+ vadd.s16 q12, q8, q12
+ vadd.s16 q9, q5, q4
+ vadd.s16 q5, q6, q10
+ vsub.s16 q10, q6, q10
+ vadd.s16 q6, q15, q13
+ vadd.s16 q8, q12, q14
+ vsub.s16 q3, q6, q3
+ vsub.s16 q12, q12, q14
+ vsub.s16 q3, q3, q1
+ vsub.s16 q1, q9, q1
+ vadd.s16 q2, q3, q2
+ vsub.s16 q15, q8, q6
+ vadd.s16 q1, q1, q2
+ vadd.s16 q8, q8, q6
+ vadd.s16 q14, q5, q3
+ vsub.s16 q9, q5, q3
+ vsub.s16 q13, q10, q2
+ vadd.s16 q10, q10, q2
+ /* Transpose */
+ vtrn.16 q8, q9
+ vsub.s16 q11, q12, q1
+ vtrn.16 q14, q15
+ vadd.s16 q12, q12, q1
+ vtrn.16 q10, q11
+ vtrn.16 q12, q13
+ vtrn.32 q9, q11
+ vtrn.32 q12, q14
+ vtrn.32 q8, q10
+ vtrn.32 q13, q15
+ vswp d28, d21
+ vswp d26, d19
+ /* 1-D IDCT, pass 2 */
+ vsub.s16 q2, q10, q14
+ vswp d30, d23
+ vadd.s16 q14, q10, q14
+ vswp d24, d17
+ vsub.s16 q1, q11, q13
+ vadd.s16 q13, q11, q13
+ vsub.s16 q5, q9, q15
+ vadd.s16 q15, q9, q15
+ vqdmulh.s16 q4, q2, XFIX_1_414213562
+ vqdmulh.s16 q6, q1, XFIX_2_613125930
+ vadd.s16 q3, q1, q1
+ vsub.s16 q1, q5, q1
+ vadd.s16 q10, q2, q4
+ vqdmulh.s16 q4, q1, XFIX_1_847759065
+ vsub.s16 q2, q15, q13
+ vadd.s16 q3, q3, q6
+ vqdmulh.s16 q6, q2, XFIX_1_414213562
+ vadd.s16 q1, q1, q4
+ vqdmulh.s16 q4, q5, XFIX_1_082392200
+ vsub.s16 q10, q10, q14
+ vadd.s16 q2, q2, q6
+ vsub.s16 q6, q8, q12
+ vadd.s16 q12, q8, q12
+ vadd.s16 q9, q5, q4
+ vadd.s16 q5, q6, q10
+ vsub.s16 q10, q6, q10
+ vadd.s16 q6, q15, q13
+ vadd.s16 q8, q12, q14
+ vsub.s16 q3, q6, q3
+ vsub.s16 q12, q12, q14
+ vsub.s16 q3, q3, q1
+ vsub.s16 q1, q9, q1
+ vadd.s16 q2, q3, q2
+ vsub.s16 q15, q8, q6
+ vadd.s16 q1, q1, q2
+ vadd.s16 q8, q8, q6
+ vadd.s16 q14, q5, q3
+ vsub.s16 q9, q5, q3
+ vsub.s16 q13, q10, q2
+ vpop {d8-d13} /* restore NEON registers */
+ vadd.s16 q10, q10, q2
+ /* Transpose */
+ vtrn.16 q8, q9
+ vsub.s16 q11, q12, q1
+ vtrn.16 q14, q15
+ vadd.s16 q12, q12, q1
+ vtrn.16 q10, q11
+ vtrn.16 q12, q13
/* Descale and range limit */
- vmov.s16 q15, #(0x80 << 5)
- vqadd.s16 q2, q2, q15
- vqadd.s16 q3, q3, q15
- vqadd.s16 q4, q4, q15
- vqadd.s16 q5, q5, q15
- vqadd.s16 q6, q6, q15
- vqadd.s16 q7, q7, q15
- vqadd.s16 q8, q8, q15
- vqadd.s16 q9, q9, q15
- vqshrun.s16 d4, q2, #5
- vqshrun.s16 d6, q3, #5
- vqshrun.s16 d8, q4, #5
- vqshrun.s16 d10, q5, #5
- vqshrun.s16 d12, q6, #5
- vqshrun.s16 d14, q7, #5
- vqshrun.s16 d16, q8, #5
- vqshrun.s16 d18, q9, #5
-
- /* Store results to the output buffer */
- .irp x, d4, d6, d8, d10, d12, d14, d16, d18
- ldr TMP, [OUTPUT_BUF], #4
- add TMP, TMP, OUTPUT_COL
- vst1.8 {\x}, [TMP]!
- .endr
-
- vpop {d8-d15}
+ vmov.s16 q0, #(0x80 << 5)
+ vtrn.32 q9, q11
+ vtrn.32 q12, q14
+ vtrn.32 q8, q10
+ vtrn.32 q13, q15
+ vswp d24, d17
+ vswp d26, d19
+ vqadd.s16 q8, q8, q0
+ vswp d28, d21
+ vqadd.s16 q9, q9, q0
+ vswp d30, d23
+ vqadd.s16 q10, q10, q0
+ vqadd.s16 q11, q11, q0
+ /* Store results to the output buffer */
+ ldmia OUTPUT_BUF!, {TMP1, TMP2}
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ vqshrun.s16 d16, q8, #5
+ vqshrun.s16 d17, q9, #5
+ vqshrun.s16 d18, q10, #5
+ vqshrun.s16 d19, q11, #5
+ vst1.8 {d16}, [TMP1]
+ vqadd.s16 q12, q12, q0
+ vqadd.s16 q13, q13, q0
+ vst1.8 {d17}, [TMP2]
+ vqadd.s16 q14, q14, q0
+ vqadd.s16 q15, q15, q0
+ ldmia OUTPUT_BUF!, {TMP1, TMP2}
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ vst1.8 {d18}, [TMP1]
+ vqshrun.s16 d20, q12, #5
+ vqshrun.s16 d21, q13, #5
+ vst1.8 {d19}, [TMP2]
+ vqshrun.s16 d22, q14, #5
+ ldmia OUTPUT_BUF, {TMP1, TMP2, TMP3, TMP4}
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ add TMP3, TMP3, OUTPUT_COL
+ add TMP4, TMP4, OUTPUT_COL
+ vst1.8 {d20}, [TMP1]
+ vqshrun.s16 d23, q15, #5
+ vst1.8 {d21}, [TMP2]
+ vst1.8 {d22}, [TMP3]
+ vst1.8 {d23}, [TMP4]
bx lr
.unreq DCT_TABLE
.unreq COEF_BLOCK
.unreq OUTPUT_BUF
.unreq OUTPUT_COL
- .unreq TMP
+ .unreq TMP1
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
.endfunc
-.purgem idct_helper
-
/*****************************************************************************/
/*