#include <config.h>
#endif
-#include <pulse/timeval.h>
+#include <pulse/rtclock.h>
+
#include <pulsecore/random.h>
#include <pulsecore/macro.h>
-#include <pulsecore/g711.h>
-#include <pulsecore/core-util.h>
+#include <pulsecore/endianmacros.h>
#include "cpu-x86.h"
#include "sample-util.h"
-#include "endianmacros.h"
+#if defined (__i386__) || defined (__amd64__)
/* in s: 2 int16_t samples
* in v: 2 int32_t volumes, fixed point 16:16
* out s: contains scaled and clamped int16_t samples.
*
* We calculate the high 32 bits of a 32x16 multiply which we then
- * clamp to 16 bits. The calulcation is:
+ * clamp to 16 bits. The calculation is:
*
* vl = (v & 0xffff)
* vh = (v >> 16)
" punpcklwd %%mm4, "#s" \n\t" /* .. | 0 | p0 | */ \
" pcmpgtw "#v", %%mm4 \n\t" /* .. | 0 | s(vl) | */ \
" pand "#s", %%mm4 \n\t" /* .. | 0 | (p0) | (vl >> 15) & p */ \
- " movq %%mm6, %%mm5 \n\t" /* .. | ffff | 0 | */ \
- " pand "#v", %%mm5 \n\t" /* .. | vh | 0 | */ \
- " por %%mm5, %%mm4 \n\t" /* .. | vh | (p0) | */ \
- " pmulhw "#s", "#v" \n\t" /* .. | 0 | vl*p0 | */ \
- " paddw %%mm4, "#v" \n\t" /* .. | vh | vl*p0 | vh + sign correct */ \
- " pslld $16, "#s" \n\t" /* .. | p0 | 0 | */ \
- " por %%mm7, "#s" \n\t" /* .. | p0 | 1 | */ \
- " pmaddwd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
+ " movq "#s", %%mm5 \n\t" \
+ " pmulhw "#v", "#s" \n\t" /* .. | 0 | vl*p0 | */ \
+ " paddw %%mm4, "#s" \n\t" /* .. | 0 | vl*p0 | + sign correct */ \
+ " pslld $16, "#s" \n\t" /* .. | vl*p0 | 0 | */ \
+ " psrld $16, "#v" \n\t" /* .. | 0 | vh | */ \
+ " psrad $16, "#s" \n\t" /* .. | vl*p0 | sign extend */ \
+ " pmaddwd %%mm5, "#v" \n\t" /* .. | p0 * vh | */ \
+ " paddd "#s", "#v" \n\t" /* .. | p0 * v0 | */ \
" packssdw "#v", "#v" \n\t" /* .. | p1*v1 | p0*v0 | */
/* approximately advances %3 = (%3 + a) % b. This function requires that
" add "#a", %3 \n\t" \
" mov %3, %4 \n\t" \
" sub "#b", %4 \n\t" \
- " cmp "#b", %3 \n\t" \
- " cmovae %4, %3 \n\t"
+ " cmovae %4, %3 \n\t"
/* swap 16 bits */
#define SWAP_16(s) \
" por %%mm4, "#s1" \n\t" /* .. | l h | */ \
" por %%mm5, "#s2" \n\t"
-static void
-pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
-{
- pa_reg_x86 channel, temp;
-
- /* the max number of samples we process at a time, this is also the max amount
- * we overread the volume array, which should have enough padding. */
- channels = MAX (4, channels);
-
- __asm__ __volatile__ (
- " xor %3, %3 \n\t"
- " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
- " pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
- " pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
- " pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
- " psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
-
- " test $1, %2 \n\t" /* check for odd samples */
- " je 2f \n\t"
-
- " movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
- " movw (%0), %4 \n\t" /* .. | p0 | */
- " movd %4, %%mm1 \n\t"
- VOLUME_32x16 (%%mm1, %%mm0)
- " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
- " movw %4, (%0) \n\t"
- " add $2, %0 \n\t"
- MOD_ADD ($1, %5)
-
- "2: \n\t"
- " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
- " test $1, %2 \n\t" /* check for odd samples */
- " je 4f \n\t"
-
- "3: \n\t" /* do samples in groups of 2 */
- " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
- " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
- VOLUME_32x16 (%%mm1, %%mm0)
- " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
- " add $4, %0 \n\t"
- MOD_ADD ($2, %5)
-
- "4: \n\t"
- " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
- " cmp $0, %2 \n\t"
- " je 6f \n\t"
-
- "5: \n\t" /* do samples in groups of 4 */
- " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
- " movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
- " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
- " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
- VOLUME_32x16 (%%mm1, %%mm0)
- VOLUME_32x16 (%%mm3, %%mm2)
- " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
- " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
- " add $8, %0 \n\t"
- MOD_ADD ($4, %5)
- " dec %2 \n\t"
- " jne 5b \n\t"
-
- "6: \n\t"
- " emms \n\t"
-
- : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
- : "r" ((pa_reg_x86)channels)
- : "cc"
- );
-}
-
-static void
-pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsigned length)
-{
- pa_reg_x86 channel, temp;
-
- /* the max number of samples we process at a time, this is also the max amount
- * we overread the volume array, which should have enough padding. */
- channels = MAX (4, channels);
-
- __asm__ __volatile__ (
- " xor %3, %3 \n\t"
- " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
- " pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
- " pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
- " pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
- " psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
-
- " test $1, %2 \n\t" /* check for odd samples */
- " je 2f \n\t"
-
- " movd (%1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
- " movw (%0), %4 \n\t" /* .. | p0 | */
- " rorw $8, %4 \n\t"
- " movd %4, %%mm1 \n\t"
- VOLUME_32x16 (%%mm1, %%mm0)
- " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
- " rorw $8, %4 \n\t"
- " movw %4, (%0) \n\t"
- " add $2, %0 \n\t"
- MOD_ADD ($1, %5)
-
- "2: \n\t"
- " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
- " test $1, %2 \n\t" /* check for odd samples */
- " je 4f \n\t"
-
- "3: \n\t" /* do samples in groups of 2 */
- " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
- " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
- SWAP_16 (%%mm1)
- VOLUME_32x16 (%%mm1, %%mm0)
- SWAP_16 (%%mm0)
- " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
- " add $4, %0 \n\t"
- MOD_ADD ($2, %5)
-
- "4: \n\t"
- " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
- " cmp $0, %2 \n\t"
- " je 6f \n\t"
-
- "5: \n\t" /* do samples in groups of 4 */
- " movq (%1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
- " movq 8(%1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
- " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
- " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
- SWAP_16_2 (%%mm1, %%mm3)
- VOLUME_32x16 (%%mm1, %%mm0)
- VOLUME_32x16 (%%mm3, %%mm2)
- SWAP_16_2 (%%mm0, %%mm2)
- " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
- " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
- " add $8, %0 \n\t"
- MOD_ADD ($4, %5)
- " dec %2 \n\t"
- " jne 5b \n\t"
-
- "6: \n\t"
- " emms \n\t"
-
- : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp)
- : "r" ((pa_reg_x86)channels)
- : "cc"
- );
+static void pa_volume_s16ne_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+ pa_reg_x86 channel, temp;
+
+ /* Channels must be at least 4, and always a multiple of the original number.
+ * This is also the max amount we overread the volume array, which should
+ * have enough padding. */
+ channels = channels == 3 ? 6 : PA_MAX (4U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%q1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " movd %4, %%mm1 \n\t"
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 6f \n\t"
+
+ "5: \n\t" /* do samples in groups of 4 */
+ " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%q1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
+ VOLUME_32x16 (%%mm1, %%mm0)
+ VOLUME_32x16 (%%mm3, %%mm2)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+ " dec %2 \n\t"
+ " jne 5b \n\t"
+
+ "6: \n\t"
+ " emms \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+ : "m" (channels)
+#else
+ : "r" ((pa_reg_x86)channels)
+#endif
+ : "cc"
+ );
}
-#undef RUN_TEST
-
-#ifdef RUN_TEST
-#define CHANNELS 2
-#define SAMPLES 1021
-#define TIMES 1000
-#define PADDING 16
-
-static void run_test (void) {
- int16_t samples[SAMPLES];
- int16_t samples_ref[SAMPLES];
- int16_t samples_orig[SAMPLES];
- int32_t volumes[CHANNELS + PADDING];
- int i, j, padding;
- pa_do_volume_func_t func;
- struct timeval start, stop;
-
- func = pa_get_volume_func (PA_SAMPLE_S16NE);
-
- printf ("checking MMX %zd\n", sizeof (samples));
-
- pa_random (samples, sizeof (samples));
- memcpy (samples_ref, samples, sizeof (samples));
- memcpy (samples_orig, samples, sizeof (samples));
-
- for (i = 0; i < CHANNELS; i++)
- volumes[i] = rand() >> 1;
- for (padding = 0; padding < PADDING; padding++, i++)
- volumes[i] = volumes[padding];
-
- func (samples_ref, volumes, CHANNELS, sizeof (samples));
- pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
- for (i = 0; i < SAMPLES; i++) {
- if (samples[i] != samples_ref[i]) {
- printf ("%d: %04x != %04x (%04x * %04x)\n", i, samples[i], samples_ref[i],
- samples_orig[i], volumes[i % CHANNELS]);
- }
- }
-
- pa_gettimeofday(&start);
- for (j = 0; j < TIMES; j++) {
- memcpy (samples, samples_orig, sizeof (samples));
- pa_volume_s16ne_mmx (samples, volumes, CHANNELS, sizeof (samples));
- }
- pa_gettimeofday(&stop);
- pa_log_info("MMX: %llu usec.", (long long unsigned int)pa_timeval_diff (&stop, &start));
-
- pa_gettimeofday(&start);
- for (j = 0; j < TIMES; j++) {
- memcpy (samples_ref, samples_orig, sizeof (samples));
- func (samples_ref, volumes, CHANNELS, sizeof (samples));
- }
- pa_gettimeofday(&stop);
- pa_log_info("ref: %llu usec.", (long long unsigned int)pa_timeval_diff (&stop, &start));
-}
+static void pa_volume_s16re_mmx(int16_t *samples, const int32_t *volumes, unsigned channels, unsigned length) {
+ pa_reg_x86 channel, temp;
+
+ /* Channels must be at least 4, and always a multiple of the original number.
+ * This is also the max amount we overread the volume array, which should
+ * have enough padding. */
+ channels = channels == 3 ? 6 : PA_MAX (4U, channels);
+
+ __asm__ __volatile__ (
+ " xor %3, %3 \n\t"
+ " sar $1, %2 \n\t" /* length /= sizeof (int16_t) */
+ " pcmpeqw %%mm6, %%mm6 \n\t" /* .. | ffff | ffff | */
+ " pcmpeqw %%mm7, %%mm7 \n\t" /* .. | ffff | ffff | */
+ " pslld $16, %%mm6 \n\t" /* .. | ffff | 0 | */
+ " psrld $31, %%mm7 \n\t" /* .. | 0 | 1 | */
+
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 2f \n\t"
+
+ " movd (%q1, %3, 4), %%mm0 \n\t" /* | v0h | v0l | */
+ " movw (%0), %w4 \n\t" /* .. | p0 | */
+ " rorw $8, %w4 \n\t"
+ " movd %4, %%mm1 \n\t"
+ VOLUME_32x16 (%%mm1, %%mm0)
+ " movd %%mm0, %4 \n\t" /* .. | p0*v0 | */
+ " rorw $8, %w4 \n\t"
+ " movw %w4, (%0) \n\t"
+ " add $2, %0 \n\t"
+ MOD_ADD ($1, %5)
+
+ "2: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 2 samples at a time */
+ " test $1, %2 \n\t" /* check for odd samples */
+ " je 4f \n\t"
+
+ "3: \n\t" /* do samples in groups of 2 */
+ " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ SWAP_16 (%%mm1)
+ VOLUME_32x16 (%%mm1, %%mm0)
+ SWAP_16 (%%mm0)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " add $4, %0 \n\t"
+ MOD_ADD ($2, %5)
+
+ "4: \n\t"
+ " sar $1, %2 \n\t" /* prepare for processing 4 samples at a time */
+ " cmp $0, %2 \n\t"
+ " je 6f \n\t"
+
+ "5: \n\t" /* do samples in groups of 4 */
+ " movq (%q1, %3, 4), %%mm0 \n\t" /* | v1h | v1l | v0h | v0l | */
+ " movq 8(%q1, %3, 4), %%mm2 \n\t" /* | v3h | v3l | v2h | v2l | */
+ " movd (%0), %%mm1 \n\t" /* .. | p1 | p0 | */
+ " movd 4(%0), %%mm3 \n\t" /* .. | p3 | p2 | */
+ SWAP_16_2 (%%mm1, %%mm3)
+ VOLUME_32x16 (%%mm1, %%mm0)
+ VOLUME_32x16 (%%mm3, %%mm2)
+ SWAP_16_2 (%%mm0, %%mm2)
+ " movd %%mm0, (%0) \n\t" /* .. | p1*v1 | p0*v0 | */
+ " movd %%mm2, 4(%0) \n\t" /* .. | p3*v3 | p2*v2 | */
+ " add $8, %0 \n\t"
+ MOD_ADD ($4, %5)
+ " dec %2 \n\t"
+ " jne 5b \n\t"
+
+ "6: \n\t"
+ " emms \n\t"
+
+ : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp)
+#if defined (__i386__)
+ : "m" (channels)
+#else
+ : "r" ((pa_reg_x86)channels)
#endif
+ : "cc"
+ );
+}
-void pa_volume_func_init_mmx (pa_cpu_x86_flag_t flags) {
- pa_log_info("Initialising MMX optimized functions.");
+#endif /* defined (__i386__) || defined (__amd64__) */
-#ifdef RUN_TEST
- run_test ();
-#endif
+void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags) {
+#if defined (__i386__) || defined (__amd64__)
+ if ((flags & PA_CPU_X86_MMX) && (flags & PA_CPU_X86_CMOV)) {
+ pa_log_info("Initialising MMX optimized volume functions.");
- pa_set_volume_func (PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
- pa_set_volume_func (PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
+ pa_set_volume_func(PA_SAMPLE_S16NE, (pa_do_volume_func_t) pa_volume_s16ne_mmx);
+ pa_set_volume_func(PA_SAMPLE_S16RE, (pa_do_volume_func_t) pa_volume_s16re_mmx);
+ }
+#endif /* defined (__i386__) || defined (__amd64__) */
}