1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00
FFmpeg/libavcodec/arm/sbcdsp_neon.S
James Cowgill aa28df74ab avcodec/arm/sbcenc: avoid callee preserved vfp registers
When compiling FFmpeg with GCC-9, some very random segfaults were
observed in code which had previously called down into the SBC encoder
NEON assembly routines. This was caused by these functions clobbering
some of the vfp callee saved registers (d8 - d15 aka q4 - q7). GCC was
using these registers to save local variables, but after these
functions returned, they would contain garbage.

Fix by reallocating the registers in the two affected functions in
the following way:
 ff_sbc_analyze_4_neon: q2-q5 => q8-q11, then q1-q4 => q8-q11
 ff_sbc_analyze_8_neon: q2-q9 => q8-q15

The reason for using these replacements is to keep closely related
sets of registers consecutively numbered which hopefully makes the
code more easy to follow. Since this commit only reallocates
registers, it should have no performance impact.

Signed-off-by: James Cowgill <jcowgill@debian.org>
Signed-off-by: Martin Storsjö <martin@martin.st>
(cherry picked from commit 50a4dff69f)
Signed-off-by: Martin Storsjö <martin@martin.st>
2022-09-20 11:22:18 +03:00

715 lines
25 KiB
ArmAsm

/*
* Bluetooth low-complexity, subband codec (SBC)
*
* Copyright (C) 2017 Aurelien Jacobs <aurel@gnuage.org>
* Copyright (C) 2008-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
* Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* SBC ARM NEON optimizations
*/
#include "libavutil/arm/asm.S"
#include "neon.S"
#define SBC_PROTO_FIXED_SCALE 16
function ff_sbc_analyze_4_neon, export=1
/* TODO: merge even and odd cases (or even merge all four calls to this
* function) in order to have only aligned reads from 'in' array
* and reduce number of load instructions */
vld1.16 {d16, d17}, [r0, :64]!
vld1.16 {d20, d21}, [r2, :128]!
vmull.s16 q0, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmull.s16 q1, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q0, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmlal.s16 q1, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q0, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmlal.s16 q1, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q0, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmlal.s16 q1, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q0, d16, d20
vmlal.s16 q1, d17, d21
vpadd.s32 d0, d0, d1
vpadd.s32 d1, d2, d3
vrshrn.s32 d0, q0, SBC_PROTO_FIXED_SCALE
vld1.16 {d16, d17, d18, d19}, [r2, :128]!
vdup.i32 d1, d0[1] /* TODO: can be eliminated */
vdup.i32 d0, d0[0] /* TODO: can be eliminated */
vmull.s16 q10, d16, d0
vmull.s16 q11, d17, d0
vmlal.s16 q10, d18, d1
vmlal.s16 q11, d19, d1
vpadd.s32 d0, d20, d21 /* TODO: can be eliminated */
vpadd.s32 d1, d22, d23 /* TODO: can be eliminated */
vst1.32 {d0, d1}, [r1, :128]
bx lr
endfunc
function ff_sbc_analyze_8_neon, export=1
/* TODO: merge even and odd cases (or even merge all four calls to this
* function) in order to have only aligned reads from 'in' array
* and reduce number of load instructions */
vld1.16 {d16, d17}, [r0, :64]!
vld1.16 {d20, d21}, [r2, :128]!
vmull.s16 q12, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmull.s16 q13, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmull.s16 q14, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmull.s16 q15, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q12, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmlal.s16 q13, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q14, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmlal.s16 q15, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q12, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmlal.s16 q13, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q14, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmlal.s16 q15, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q12, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmlal.s16 q13, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q14, d18, d22
vld1.16 {d16, d17}, [r0, :64]!
vmlal.s16 q15, d19, d23
vld1.16 {d20, d21}, [r2, :128]!
vmlal.s16 q12, d16, d20
vld1.16 {d18, d19}, [r0, :64]!
vmlal.s16 q13, d17, d21
vld1.16 {d22, d23}, [r2, :128]!
vmlal.s16 q14, d18, d22
vmlal.s16 q15, d19, d23
vpadd.s32 d0, d24, d25
vpadd.s32 d1, d26, d27
vpadd.s32 d2, d28, d29
vpadd.s32 d3, d30, d31
vrshr.s32 q0, q0, SBC_PROTO_FIXED_SCALE
vrshr.s32 q1, q1, SBC_PROTO_FIXED_SCALE
vmovn.s32 d0, q0
vmovn.s32 d1, q1
vdup.i32 d3, d1[1] /* TODO: can be eliminated */
vdup.i32 d2, d1[0] /* TODO: can be eliminated */
vdup.i32 d1, d0[1] /* TODO: can be eliminated */
vdup.i32 d0, d0[0] /* TODO: can be eliminated */
vld1.16 {d16, d17}, [r2, :128]!
vmull.s16 q12, d16, d0
vld1.16 {d18, d19}, [r2, :128]!
vmull.s16 q13, d17, d0
vmull.s16 q14, d18, d0
vmull.s16 q15, d19, d0
vld1.16 {d16, d17}, [r2, :128]!
vmlal.s16 q12, d16, d1
vld1.16 {d18, d19}, [r2, :128]!
vmlal.s16 q13, d17, d1
vmlal.s16 q14, d18, d1
vmlal.s16 q15, d19, d1
vld1.16 {d16, d17}, [r2, :128]!
vmlal.s16 q12, d16, d2
vld1.16 {d18, d19}, [r2, :128]!
vmlal.s16 q13, d17, d2
vmlal.s16 q14, d18, d2
vmlal.s16 q15, d19, d2
vld1.16 {d16, d17}, [r2, :128]!
vmlal.s16 q12, d16, d3
vld1.16 {d18, d19}, [r2, :128]!
vmlal.s16 q13, d17, d3
vmlal.s16 q14, d18, d3
vmlal.s16 q15, d19, d3
vpadd.s32 d0, d24, d25 /* TODO: can be eliminated */
vpadd.s32 d1, d26, d27 /* TODO: can be eliminated */
vpadd.s32 d2, d28, d29 /* TODO: can be eliminated */
vpadd.s32 d3, d30, d31 /* TODO: can be eliminated */
vst1.32 {d0, d1, d2, d3}, [r1, :128]
bx lr
endfunc
function ff_sbc_calc_scalefactors_neon, export=1
@ parameters
@ r0 = sb_sample_f
@ r1 = scale_factor
@ r2 = blocks
@ r3 = channels
@ r4 = subbands
@ local variables
@ r5 = in_loop_1
@ r6 = in
@ r7 = out_loop_1
@ r8 = out
@ r9 = ch
@ r10 = sb
@ r11 = inc
@ r12 = blk
push {r1-r2, r4-r12}
ldr r4, [sp, #44]
mov r11, #64
mov r9, #0
1:
add r5, r0, r9, lsl#5
add r7, r1, r9, lsl#5
mov r10, #0
2:
add r6, r5, r10, lsl#2
add r8, r7, r10, lsl#2
mov r12, r2
vmov.s32 q0, #0
vmov.s32 q1, #0x8000 @ 1 << SCALE_OUT_BITS
vmov.s32 q14, #1
vmov.s32 q15, #16 @ 31 - SCALE_OUT_BITS
vadd.s32 q1, q1, q14
3:
vld1.32 {d16, d17}, [r6, :128], r11
vabs.s32 q8, q8
vld1.32 {d18, d19}, [r6, :128], r11
vabs.s32 q9, q9
vld1.32 {d20, d21}, [r6, :128], r11
vabs.s32 q10, q10
vld1.32 {d22, d23}, [r6, :128], r11
vabs.s32 q11, q11
vmax.s32 q0, q0, q8
vmax.s32 q1, q1, q9
vmax.s32 q0, q0, q10
vmax.s32 q1, q1, q11
subs r12, r12, #4
bgt 3b
vmax.s32 q0, q0, q1
vsub.s32 q0, q0, q14
vclz.s32 q0, q0
vsub.s32 q0, q15, q0
vst1.32 {d0, d1}, [r8, :128]
add r10, r10, #4
cmp r10, r4
blt 2b
add r9, r9, #1
cmp r9, r3
blt 1b
pop {r1-r2, r4-r12}
bx lr
endfunc
/*
* constants: q13 = (31 - SCALE_OUT_BITS)
* q14 = 1
* input: q0 - ((1 << SCALE_OUT_BITS) + 1)
* r5 - samples for channel 0
* r6 - samples for shannel 1
* output: q0, q1 - scale factors without joint stereo
* q2, q3 - scale factors with joint stereo
* q15 - joint stereo selection mask
*/
.macro calc_scalefactors
vmov.s32 q1, q0
vmov.s32 q2, q0
vmov.s32 q3, q0
mov r3, r2
1:
vld1.32 {d18, d19}, [r6, :128], r11
vbic.s32 q11, q9, q14
vld1.32 {d16, d17}, [r5, :128], r11
vhadd.s32 q10, q8, q11
vhsub.s32 q11, q8, q11
vabs.s32 q8, q8
vabs.s32 q9, q9
vabs.s32 q10, q10
vabs.s32 q11, q11
vmax.s32 q0, q0, q8
vmax.s32 q1, q1, q9
vmax.s32 q2, q2, q10
vmax.s32 q3, q3, q11
subs r3, r3, #1
bgt 1b
vsub.s32 q0, q0, q14
vsub.s32 q1, q1, q14
vsub.s32 q2, q2, q14
vsub.s32 q3, q3, q14
vclz.s32 q0, q0
vclz.s32 q1, q1
vclz.s32 q2, q2
vclz.s32 q3, q3
vsub.s32 q0, q13, q0
vsub.s32 q1, q13, q1
vsub.s32 q2, q13, q2
vsub.s32 q3, q13, q3
.endm
/*
* constants: q14 = 1
* input: q15 - joint stereo selection mask
* r5 - value set by calc_scalefactors macro
* r6 - value set by calc_scalefactors macro
*/
.macro update_joint_stereo_samples
sub r8, r6, r11
sub r7, r5, r11
sub r6, r6, r11, asl #1
sub r5, r5, r11, asl #1
vld1.32 {d18, d19}, [r6, :128]
vbic.s32 q11, q9, q14
vld1.32 {d16, d17}, [r5, :128]
vld1.32 {d2, d3}, [r8, :128]
vbic.s32 q3, q1, q14
vld1.32 {d0, d1}, [r7, :128]
vhsub.s32 q10, q8, q11
vhadd.s32 q11, q8, q11
vhsub.s32 q2, q0, q3
vhadd.s32 q3, q0, q3
vbif.s32 q10, q9, q15
vbif.s32 d22, d16, d30
sub r11, r10, r11, asl #1
sub r3, r2, #2
2:
vbif.s32 d23, d17, d31
vst1.32 {d20, d21}, [r6, :128], r11
vbif.s32 d4, d2, d30
vld1.32 {d18, d19}, [r6, :128]
vbif.s32 d5, d3, d31
vst1.32 {d22, d23}, [r5, :128], r11
vbif.s32 d6, d0, d30
vld1.32 {d16, d17}, [r5, :128]
vbif.s32 d7, d1, d31
vst1.32 {d4, d5}, [r8, :128], r11
vbic.s32 q11, q9, q14
vld1.32 {d2, d3}, [r8, :128]
vst1.32 {d6, d7}, [r7, :128], r11
vbic.s32 q3, q1, q14
vld1.32 {d0, d1}, [r7, :128]
vhsub.s32 q10, q8, q11
vhadd.s32 q11, q8, q11
vhsub.s32 q2, q0, q3
vhadd.s32 q3, q0, q3
vbif.s32 q10, q9, q15
vbif.s32 d22, d16, d30
subs r3, r3, #2
bgt 2b
sub r11, r10, r11, asr #1
vbif.s32 d23, d17, d31
vst1.32 {d20, d21}, [r6, :128]
vbif.s32 q2, q1, q15
vst1.32 {d22, d23}, [r5, :128]
vbif.s32 q3, q0, q15
vst1.32 {d4, d5}, [r8, :128]
vst1.32 {d6, d7}, [r7, :128]
.endm
function ff_sbc_calc_scalefactors_j_neon, export=1
@ parameters
@ r0 = in = sb_sample_f
@ r1 = out = scale_factor
@ r2 = blocks
@ r3 = subbands
@ local variables
@ r4 = consts = ff_sbcdsp_joint_bits_mask
@ r5 = in0
@ r6 = in1
@ r7 = out0
@ r8 = out1
@ r10 = zero
@ r11 = inc
@ return r0 = joint
push {r3-r11}
movrelx r4, X(ff_sbcdsp_joint_bits_mask)
mov r10, #0
mov r11, #64
vmov.s32 q14, #1
vmov.s32 q13, #16 @ 31 - SCALE_OUT_BITS
cmp r3, #4
bne 8f
4: @ 4 subbands
add r5, r0, #0
add r6, r0, #32
add r7, r1, #0
add r8, r1, #32
vmov.s32 q0, #0x8000 @ 1 << SCALE_OUT_BITS
vadd.s32 q0, q0, q14
calc_scalefactors
@ check whether to use joint stereo for subbands 0, 1, 2
vadd.s32 q15, q0, q1
vadd.s32 q9, q2, q3
vmov.s32 d31[1], r10 @ last subband -> no joint
vld1.32 {d16, d17}, [r4, :128]!
vcgt.s32 q15, q15, q9
@ calculate and save to memory 'joint' variable
@ update and save scale factors to memory
vand.s32 q8, q8, q15
vbit.s32 q0, q2, q15
vpadd.s32 d16, d16, d17
vbit.s32 q1, q3, q15
vpadd.s32 d16, d16, d16
vst1.32 {d0, d1}, [r7, :128]
vst1.32 {d2, d3}, [r8, :128]
vmov.32 r0, d16[0]
update_joint_stereo_samples
b 9f
8: @ 8 subbands
add r5, r0, #16
add r6, r0, #48
add r7, r1, #16
add r8, r1, #48
vmov.s32 q0, #0x8000 @ 1 << SCALE_OUT_BITS
vadd.s32 q0, q0, q14
calc_scalefactors
@ check whether to use joint stereo for subbands 4, 5, 6
vadd.s32 q15, q0, q1
vadd.s32 q9, q2, q3
vmov.s32 d31[1], r10 @ last subband -> no joint
vld1.32 {d16, d17}, [r4, :128]!
vcgt.s32 q15, q15, q9
@ calculate part of 'joint' variable and save it to d24
@ update and save scale factors to memory
vand.s32 q8, q8, q15
vbit.s32 q0, q2, q15
vpadd.s32 d16, d16, d17
vbit.s32 q1, q3, q15
vst1.32 {d0, d1}, [r7, :128]
vst1.32 {d2, d3}, [r8, :128]
vpadd.s32 d24, d16, d16
update_joint_stereo_samples
add r5, r0, #0
add r6, r0, #32
add r7, r1, #0
add r8, r1, #32
vmov.s32 q0, #0x8000 @ 1 << SCALE_OUT_BITS
vadd.s32 q0, q0, q14
calc_scalefactors
@ check whether to use joint stereo for subbands 0, 1, 2, 3
vadd.s32 q15, q0, q1
vadd.s32 q9, q2, q3
vld1.32 {d16, d17}, [r4, :128]!
vcgt.s32 q15, q15, q9
@ combine last part of 'joint' with d24 and save to memory
@ update and save scale factors to memory
vand.s32 q8, q8, q15
vbit.s32 q0, q2, q15
vpadd.s32 d16, d16, d17
vbit.s32 q1, q3, q15
vpadd.s32 d16, d16, d16
vst1.32 {d0, d1}, [r7, :128]
vadd.s32 d16, d16, d24
vst1.32 {d2, d3}, [r8, :128]
vmov.32 r0, d16[0]
update_joint_stereo_samples
9:
pop {r3-r11}
bx lr
endfunc
function ff_sbc_enc_process_input_4s_neon, export=1
@ parameters
@ r0 = positioin
@ r1 = pcm
@ r2 = X
@ r3 = nsamples
@ r4 = nchannels
@ local variables
@ r5 = ff_sbc_input_perm_4
@ r6 = src / x
@ r7 = dst / y
push {r1, r3-r7}
ldr r4, [sp, #24]
movrelx r5, X(ff_sbc_input_perm_4)
@ handle X buffer wraparound
cmp r0, r3
bge 1f @ if (position < nsamples)
add r7, r2, #576 @ &X[0][SBC_X_BUFFER_SIZE - 40]
add r6, r2, r0, lsl#1 @ &X[0][position]
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0}, [r6, :64]!
vst1.16 {d0}, [r7, :64]!
cmp r4, #1
ble 2f @ if (nchannels > 1)
add r7, r2, #1232 @ &X[1][SBC_X_BUFFER_SIZE - 40]
add r6, r2, #656
add r6, r6, r0, lsl#1 @ &X[1][position]
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0}, [r6, :64]!
vst1.16 {d0}, [r7, :64]!
2:
mov r0, #288 @ SBC_X_BUFFER_SIZE - 40
1:
add r6, r2, r0, lsl#1 @ &X[0][position]
add r7, r6, #656 @ &X[1][position]
cmp r4, #1
ble 8f @ if (nchannels > 1)
tst r1, #1
beq 7f @ if (pcm & 1)
@ poor 'pcm' alignment
vld1.8 {d0, d1}, [r5, :128]
1:
sub r6, r6, #16
sub r7, r7, #16
sub r0, r0, #8
vld1.8 {d4, d5}, [r1]!
vuzp.16 d4, d5
vld1.8 {d20, d21}, [r1]!
vuzp.16 d20, d21
vswp d5, d20
vtbl.8 d16, {d4, d5}, d0
vtbl.8 d17, {d4, d5}, d1
vtbl.8 d18, {d20, d21}, d0
vtbl.8 d19, {d20, d21}, d1
vst1.16 {d16, d17}, [r6, :128]
vst1.16 {d18, d19}, [r7, :128]
subs r3, r3, #8
bgt 1b
b 9f
7:
@ proper 'pcm' alignment
vld1.8 {d0, d1}, [r5, :128]
1:
sub r6, r6, #16
sub r7, r7, #16
sub r0, r0, #8
vld2.16 {d4, d5}, [r1]!
vld2.16 {d20, d21}, [r1]!
vswp d5, d20
vtbl.8 d16, {d4, d5}, d0
vtbl.8 d17, {d4, d5}, d1
vtbl.8 d18, {d20, d21}, d0
vtbl.8 d19, {d20, d21}, d1
vst1.16 {d16, d17}, [r6, :128]
vst1.16 {d18, d19}, [r7, :128]
subs r3, r3, #8
bgt 1b
b 9f
8:
@ mono
vld1.8 {d0, d1}, [r5, :128]
1:
sub r6, r6, #16
sub r0, r0, #8
vld1.8 {d4, d5}, [r1]!
vtbl.8 d16, {d4, d5}, d0
vtbl.8 d17, {d4, d5}, d1
vst1.16 {d16, d17}, [r6, :128]
subs r3, r3, #8
bgt 1b
9:
pop {r1, r3-r7}
bx lr
endfunc
function ff_sbc_enc_process_input_8s_neon, export=1
@ parameters
@ r0 = positioin
@ r1 = pcm
@ r2 = X
@ r3 = nsamples
@ r4 = nchannels
@ local variables
@ r5 = ff_sbc_input_perm_8
@ r6 = src
@ r7 = dst
push {r1, r3-r7}
ldr r4, [sp, #24]
movrelx r5, X(ff_sbc_input_perm_8)
@ handle X buffer wraparound
cmp r0, r3
bge 1f @ if (position < nsamples)
add r7, r2, #512 @ &X[0][SBC_X_BUFFER_SIZE - 72]
add r6, r2, r0, lsl#1 @ &X[0][position]
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1}, [r6, :128]!
vst1.16 {d0, d1}, [r7, :128]!
cmp r4, #1
ble 2f @ if (nchannels > 1)
add r7, r2, #1168 @ &X[1][SBC_X_BUFFER_SIZE - 72]
add r6, r2, #656
add r6, r6, r0, lsl#1 @ &X[1][position]
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1, d2, d3}, [r6, :128]!
vst1.16 {d0, d1, d2, d3}, [r7, :128]!
vld1.16 {d0, d1}, [r6, :128]!
vst1.16 {d0, d1}, [r7, :128]!
2:
mov r0, #256 @ SBC_X_BUFFER_SIZE - 72
1:
add r6, r2, r0, lsl#1 @ &X[0][position]
add r7, r6, #656 @ &X[1][position]
cmp r4, #1
ble 8f @ if (nchannels > 1)
tst r1, #1
beq 7f @ if (pcm & 1)
@ poor 'pcm' alignment
vld1.8 {d0, d1, d2, d3}, [r5, :128]
1:
sub r6, r6, #32
sub r7, r7, #32
sub r0, r0, #16
vld1.8 {d4, d5, d6, d7}, [r1]!
vuzp.16 q2, q3
vld1.8 {d20, d21, d22, d23}, [r1]!
vuzp.16 q10, q11
vswp q3, q10
vtbl.8 d16, {d4, d5, d6, d7}, d0
vtbl.8 d17, {d4, d5, d6, d7}, d1
vtbl.8 d18, {d4, d5, d6, d7}, d2
vtbl.8 d19, {d4, d5, d6, d7}, d3
vst1.16 {d16, d17, d18, d19}, [r6, :128]
vtbl.8 d16, {d20, d21, d22, d23}, d0
vtbl.8 d17, {d20, d21, d22, d23}, d1
vtbl.8 d18, {d20, d21, d22, d23}, d2
vtbl.8 d19, {d20, d21, d22, d23}, d3
vst1.16 {d16, d17, d18, d19}, [r7, :128]
subs r3, r3, #16
bgt 1b
b 9f
7:
@ proper 'pcm' alignment
vld1.8 {d0, d1, d2, d3}, [r5, :128]
1:
sub r6, r6, #32
sub r7, r7, #32
sub r0, r0, #16
vld2.16 {d4, d5, d6, d7}, [r1]!
vld2.16 {d20, d21, d22, d23}, [r1]!
vswp q3, q10
vtbl.8 d16, {d4, d5, d6, d7}, d0
vtbl.8 d17, {d4, d5, d6, d7}, d1
vtbl.8 d18, {d4, d5, d6, d7}, d2
vtbl.8 d19, {d4, d5, d6, d7}, d3
vst1.16 {d16, d17, d18, d19}, [r6, :128]
vtbl.8 d16, {d20, d21, d22, d23}, d0
vtbl.8 d17, {d20, d21, d22, d23}, d1
vtbl.8 d18, {d20, d21, d22, d23}, d2
vtbl.8 d19, {d20, d21, d22, d23}, d3
vst1.16 {d16, d17, d18, d19}, [r7, :128]
subs r3, r3, #16
bgt 1b
b 9f
8:
@ mono
vld1.8 {d0, d1, d2, d3}, [r5, :128]
1:
sub r6, r6, #32
sub r0, r0, #16
vld1.8 {d4, d5, d6, d7}, [r1]!
vtbl.8 d16, {d4, d5, d6, d7}, d0
vtbl.8 d17, {d4, d5, d6, d7}, d1
vtbl.8 d18, {d4, d5, d6, d7}, d2
vtbl.8 d19, {d4, d5, d6, d7}, d3
vst1.16 {d16, d17, d18, d19}, [r6, :128]
subs r3, r3, #16
bgt 1b
9:
pop {r1, r3-r7}
bx lr
endfunc