c is 1.9x faster than previous c (on various x86 cpus), sse is 1.6x faster than previous sse. Originally committed as revision 14698 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.5
| @@ -388,6 +388,8 @@ OBJS += i386/fdct_mmx.o \ | |||
| i386/simple_idct_mmx.o \ | |||
| i386/idct_mmx_xvid.o \ | |||
| i386/idct_sse2_xvid.o \ | |||
| OBJS-$(HAVE_YASM) += i386/fft_mmx.o \ | |||
| i386/fft_sse.o \ | |||
| i386/fft_3dn.o \ | |||
| i386/fft_3dn2.o \ | |||
| @@ -639,6 +639,8 @@ typedef struct FFTContext { | |||
| uint16_t *revtab; | |||
| FFTComplex *exptab; | |||
| FFTComplex *exptab1; /* only used by SSE code */ | |||
| FFTComplex *tmp_buf; | |||
| void (*fft_permute)(struct FFTContext *s, FFTComplex *z); | |||
| void (*fft_calc)(struct FFTContext *s, FFTComplex *z); | |||
| void (*imdct_calc)(struct MDCTContext *s, FFTSample *output, | |||
| const FFTSample *input, FFTSample *tmp); | |||
| @@ -647,13 +649,18 @@ typedef struct FFTContext { | |||
| } FFTContext; | |||
| int ff_fft_init(FFTContext *s, int nbits, int inverse); | |||
| void ff_fft_permute(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_permute_c(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_permute_sse(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_calc_c(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_calc_sse(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_calc_3dn(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_calc_3dn2(FFTContext *s, FFTComplex *z); | |||
| void ff_fft_calc_altivec(FFTContext *s, FFTComplex *z); | |||
| static inline void ff_fft_permute(FFTContext *s, FFTComplex *z) | |||
| { | |||
| s->fft_permute(s, z); | |||
| } | |||
| static inline void ff_fft_calc(FFTContext *s, FFTComplex *z) | |||
| { | |||
| s->fft_calc(s, z); | |||
| @@ -1,6 +1,8 @@ | |||
| /* | |||
| * FFT/IFFT transforms | |||
| * Copyright (c) 2008 Loren Merritt | |||
| * Copyright (c) 2002 Fabrice Bellard. | |||
| * Partly based on libdjbfft by D. J. Bernstein | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| @@ -26,6 +28,36 @@ | |||
| #include "dsputil.h" | |||
| /* cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse */ | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_16[8]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_32[16]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_64[32]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_128[64]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_256[128]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_512[256]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_1024[512]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_2048[1024]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_4096[2048]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_8192[4096]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_16384[8192]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_32768[16384]); | |||
| DECLARE_ALIGNED_16(FFTSample, ff_cos_65536[32768]); | |||
| static FFTSample *ff_cos_tabs[] = { | |||
| ff_cos_16, ff_cos_32, ff_cos_64, ff_cos_128, ff_cos_256, ff_cos_512, ff_cos_1024, | |||
| ff_cos_2048, ff_cos_4096, ff_cos_8192, ff_cos_16384, ff_cos_32768, ff_cos_65536, | |||
| }; | |||
| static int split_radix_permutation(int i, int n, int inverse) | |||
| { | |||
| int m; | |||
| if(n <= 2) return i&1; | |||
| m = n >> 1; | |||
| if(!(i&m)) return split_radix_permutation(i, m, inverse)*2; | |||
| m >>= 1; | |||
| if(inverse == !(i&m)) return split_radix_permutation(i, m, inverse)*4 + 1; | |||
| else return split_radix_permutation(i, m, inverse)*4 - 1; | |||
| } | |||
| /** | |||
| * The size of the FFT is 2^nbits. If inverse is TRUE, inverse FFT is | |||
| * done | |||
| @@ -34,12 +66,15 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) | |||
| { | |||
| int i, j, m, n; | |||
| float alpha, c1, s1, s2; | |||
| int shuffle = 0; | |||
| int split_radix = 1; | |||
| int av_unused has_vectors; | |||
| if (nbits < 2 || nbits > 16) | |||
| goto fail; | |||
| s->nbits = nbits; | |||
| n = 1 << nbits; | |||
| s->tmp_buf = NULL; | |||
| s->exptab = av_malloc((n / 2) * sizeof(FFTComplex)); | |||
| if (!s->exptab) | |||
| goto fail; | |||
| @@ -50,50 +85,62 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) | |||
| s2 = inverse ? 1.0 : -1.0; | |||
| for(i=0;i<(n/2);i++) { | |||
| alpha = 2 * M_PI * (float)i / (float)n; | |||
| c1 = cos(alpha); | |||
| s1 = sin(alpha) * s2; | |||
| s->exptab[i].re = c1; | |||
| s->exptab[i].im = s1; | |||
| } | |||
| s->fft_permute = ff_fft_permute_c; | |||
| s->fft_calc = ff_fft_calc_c; | |||
| s->imdct_calc = ff_imdct_calc; | |||
| s->imdct_half = ff_imdct_half; | |||
| s->exptab1 = NULL; | |||
| #ifdef HAVE_MMX | |||
| #if defined HAVE_MMX && defined HAVE_YASM | |||
| has_vectors = mm_support(); | |||
| shuffle = 1; | |||
| if (has_vectors & MM_3DNOWEXT) { | |||
| /* 3DNowEx for K7/K8 */ | |||
| if (has_vectors & MM_SSE) { | |||
| /* SSE for P3/P4/K8 */ | |||
| s->imdct_calc = ff_imdct_calc_sse; | |||
| s->imdct_half = ff_imdct_half_sse; | |||
| s->fft_permute = ff_fft_permute_sse; | |||
| s->fft_calc = ff_fft_calc_sse; | |||
| } else if (has_vectors & MM_3DNOWEXT) { | |||
| /* 3DNowEx for K7 */ | |||
| s->imdct_calc = ff_imdct_calc_3dn2; | |||
| s->imdct_half = ff_imdct_half_3dn2; | |||
| s->fft_calc = ff_fft_calc_3dn2; | |||
| } else if (has_vectors & MM_3DNOW) { | |||
| /* 3DNow! for K6-2/3 */ | |||
| s->fft_calc = ff_fft_calc_3dn; | |||
| } else if (has_vectors & MM_SSE) { | |||
| /* SSE for P3/P4 */ | |||
| s->imdct_calc = ff_imdct_calc_sse; | |||
| s->imdct_half = ff_imdct_half_sse; | |||
| s->fft_calc = ff_fft_calc_sse; | |||
| } else { | |||
| shuffle = 0; | |||
| } | |||
| #elif defined HAVE_ALTIVEC && !defined ALTIVEC_USE_REFERENCE_C_CODE | |||
| has_vectors = mm_support(); | |||
| if (has_vectors & MM_ALTIVEC) { | |||
| s->fft_calc = ff_fft_calc_altivec; | |||
| shuffle = 1; | |||
| split_radix = 0; | |||
| } | |||
| #endif | |||
| /* compute constant table for HAVE_SSE version */ | |||
| if (shuffle) { | |||
| if (split_radix) { | |||
| for(j=4; j<=nbits; j++) { | |||
| int m = 1<<j; | |||
| double freq = 2*M_PI/m; | |||
| FFTSample *tab = ff_cos_tabs[j-4]; | |||
| for(i=0; i<=m/4; i++) | |||
| tab[i] = cos(i*freq); | |||
| for(i=1; i<m/4; i++) | |||
| tab[m/2-i] = tab[i]; | |||
| } | |||
| for(i=0; i<n; i++) | |||
| s->revtab[-split_radix_permutation(i, n, s->inverse) & (n-1)] = i; | |||
| s->tmp_buf = av_malloc(n * sizeof(FFTComplex)); | |||
| } else { | |||
| int np, nblocks, np2, l; | |||
| FFTComplex *q; | |||
| for(i=0; i<(n/2); i++) { | |||
| alpha = 2 * M_PI * (float)i / (float)n; | |||
| c1 = cos(alpha); | |||
| s1 = sin(alpha) * s2; | |||
| s->exptab[i].re = c1; | |||
| s->exptab[i].im = s1; | |||
| } | |||
| np = 1 << nbits; | |||
| nblocks = np >> 3; | |||
| np2 = np >> 1; | |||
| @@ -116,7 +163,6 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) | |||
| nblocks = nblocks >> 1; | |||
| } while (nblocks != 0); | |||
| av_freep(&s->exptab); | |||
| } | |||
| /* compute bit reverse table */ | |||
| @@ -127,126 +173,35 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) | |||
| } | |||
| s->revtab[i]=m; | |||
| } | |||
| } | |||
| return 0; | |||
| fail: | |||
| av_freep(&s->revtab); | |||
| av_freep(&s->exptab); | |||
| av_freep(&s->exptab1); | |||
| av_freep(&s->tmp_buf); | |||
| return -1; | |||
| } | |||
| /* butter fly op */ | |||
| #define BF(pre, pim, qre, qim, pre1, pim1, qre1, qim1) \ | |||
| {\ | |||
| FFTSample ax, ay, bx, by;\ | |||
| bx=pre1;\ | |||
| by=pim1;\ | |||
| ax=qre1;\ | |||
| ay=qim1;\ | |||
| pre = (bx + ax);\ | |||
| pim = (by + ay);\ | |||
| qre = (bx - ax);\ | |||
| qim = (by - ay);\ | |||
| } | |||
| #define MUL16(a,b) ((a) * (b)) | |||
| #define CMUL(pre, pim, are, aim, bre, bim) \ | |||
| {\ | |||
| pre = (MUL16(are, bre) - MUL16(aim, bim));\ | |||
| pim = (MUL16(are, bim) + MUL16(bre, aim));\ | |||
| } | |||
| /** | |||
| * Do a complex FFT with the parameters defined in ff_fft_init(). The | |||
| * input data must be permuted before with s->revtab table. No | |||
| * 1.0/sqrt(n) normalization is done. | |||
| */ | |||
| void ff_fft_calc_c(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int ln = s->nbits; | |||
| int j, np, np2; | |||
| int nblocks, nloops; | |||
| register FFTComplex *p, *q; | |||
| FFTComplex *exptab = s->exptab; | |||
| int l; | |||
| FFTSample tmp_re, tmp_im; | |||
| np = 1 << ln; | |||
| /* pass 0 */ | |||
| p=&z[0]; | |||
| j=(np >> 1); | |||
| do { | |||
| BF(p[0].re, p[0].im, p[1].re, p[1].im, | |||
| p[0].re, p[0].im, p[1].re, p[1].im); | |||
| p+=2; | |||
| } while (--j != 0); | |||
| /* pass 1 */ | |||
| p=&z[0]; | |||
| j=np >> 2; | |||
| if (s->inverse) { | |||
| do { | |||
| BF(p[0].re, p[0].im, p[2].re, p[2].im, | |||
| p[0].re, p[0].im, p[2].re, p[2].im); | |||
| BF(p[1].re, p[1].im, p[3].re, p[3].im, | |||
| p[1].re, p[1].im, -p[3].im, p[3].re); | |||
| p+=4; | |||
| } while (--j != 0); | |||
| } else { | |||
| do { | |||
| BF(p[0].re, p[0].im, p[2].re, p[2].im, | |||
| p[0].re, p[0].im, p[2].re, p[2].im); | |||
| BF(p[1].re, p[1].im, p[3].re, p[3].im, | |||
| p[1].re, p[1].im, p[3].im, -p[3].re); | |||
| p+=4; | |||
| } while (--j != 0); | |||
| } | |||
| /* pass 2 .. ln-1 */ | |||
| nblocks = np >> 3; | |||
| nloops = 1 << 2; | |||
| np2 = np >> 1; | |||
| do { | |||
| p = z; | |||
| q = z + nloops; | |||
| for (j = 0; j < nblocks; ++j) { | |||
| BF(p->re, p->im, q->re, q->im, | |||
| p->re, p->im, q->re, q->im); | |||
| p++; | |||
| q++; | |||
| for(l = nblocks; l < np2; l += nblocks) { | |||
| CMUL(tmp_re, tmp_im, exptab[l].re, exptab[l].im, q->re, q->im); | |||
| BF(p->re, p->im, q->re, q->im, | |||
| p->re, p->im, tmp_re, tmp_im); | |||
| p++; | |||
| q++; | |||
| } | |||
| p += nloops; | |||
| q += nloops; | |||
| } | |||
| nblocks = nblocks >> 1; | |||
| nloops = nloops << 1; | |||
| } while (nblocks != 0); | |||
| } | |||
| /** | |||
| * Do the permutation needed BEFORE calling ff_fft_calc() | |||
| */ | |||
| void ff_fft_permute(FFTContext *s, FFTComplex *z) | |||
| void ff_fft_permute_c(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int j, k, np; | |||
| FFTComplex tmp; | |||
| const uint16_t *revtab = s->revtab; | |||
| np = 1 << s->nbits; | |||
| if (s->tmp_buf) { | |||
| /* TODO: handle split-radix permute in a more optimal way, probably in-place */ | |||
| for(j=0;j<np;j++) s->tmp_buf[revtab[j]] = z[j]; | |||
| memcpy(z, s->tmp_buf, np * sizeof(FFTComplex)); | |||
| return; | |||
| } | |||
| /* reverse */ | |||
| np = 1 << s->nbits; | |||
| for(j=0;j<np;j++) { | |||
| k = revtab[j]; | |||
| if (k < j) { | |||
| @@ -262,5 +217,169 @@ void ff_fft_end(FFTContext *s) | |||
| av_freep(&s->revtab); | |||
| av_freep(&s->exptab); | |||
| av_freep(&s->exptab1); | |||
| av_freep(&s->tmp_buf); | |||
| } | |||
| #define sqrthalf (float)M_SQRT1_2 | |||
| #define BF(x,y,a,b) {\ | |||
| x = a - b;\ | |||
| y = a + b;\ | |||
| } | |||
| #define BUTTERFLIES(a0,a1,a2,a3) {\ | |||
| BF(t3, t5, t5, t1);\ | |||
| BF(a2.re, a0.re, a0.re, t5);\ | |||
| BF(a3.im, a1.im, a1.im, t3);\ | |||
| BF(t4, t6, t2, t6);\ | |||
| BF(a3.re, a1.re, a1.re, t4);\ | |||
| BF(a2.im, a0.im, a0.im, t6);\ | |||
| } | |||
| // force loading all the inputs before storing any. | |||
| // this is slightly slower for small data, but avoids store->load aliasing | |||
| // for addresses separated by large powers of 2. | |||
| #define BUTTERFLIES_BIG(a0,a1,a2,a3) {\ | |||
| FFTSample r0=a0.re, i0=a0.im, r1=a1.re, i1=a1.im;\ | |||
| BF(t3, t5, t5, t1);\ | |||
| BF(a2.re, a0.re, r0, t5);\ | |||
| BF(a3.im, a1.im, i1, t3);\ | |||
| BF(t4, t6, t2, t6);\ | |||
| BF(a3.re, a1.re, r1, t4);\ | |||
| BF(a2.im, a0.im, i0, t6);\ | |||
| } | |||
| #define TRANSFORM(a0,a1,a2,a3,wre,wim) {\ | |||
| t1 = a2.re * wre + a2.im * wim;\ | |||
| t2 = a2.im * wre - a2.re * wim;\ | |||
| t5 = a3.re * wre - a3.im * wim;\ | |||
| t6 = a3.im * wre + a3.re * wim;\ | |||
| BUTTERFLIES(a0,a1,a2,a3)\ | |||
| } | |||
| #define TRANSFORM_ZERO(a0,a1,a2,a3) {\ | |||
| t1 = a2.re;\ | |||
| t2 = a2.im;\ | |||
| t5 = a3.re;\ | |||
| t6 = a3.im;\ | |||
| BUTTERFLIES(a0,a1,a2,a3)\ | |||
| } | |||
| /* z[0...8n-1], w[1...2n-1] */ | |||
| #define PASS(name)\ | |||
| static void name(FFTComplex *z, const FFTSample *wre, unsigned int n)\ | |||
| {\ | |||
| FFTSample t1, t2, t3, t4, t5, t6;\ | |||
| int o1 = 2*n;\ | |||
| int o2 = 4*n;\ | |||
| int o3 = 6*n;\ | |||
| const FFTSample *wim = wre+o1;\ | |||
| n--;\ | |||
| \ | |||
| TRANSFORM_ZERO(z[0],z[o1],z[o2],z[o3]);\ | |||
| TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ | |||
| do {\ | |||
| z += 2;\ | |||
| wre += 2;\ | |||
| wim -= 2;\ | |||
| TRANSFORM(z[0],z[o1],z[o2],z[o3],wre[0],wim[0]);\ | |||
| TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ | |||
| } while(--n);\ | |||
| } | |||
| PASS(pass) | |||
| #undef BUTTERFLIES | |||
| #define BUTTERFLIES BUTTERFLIES_BIG | |||
| PASS(pass_big) | |||
| #define DECL_FFT(n,n2,n4)\ | |||
| static void fft##n(FFTComplex *z)\ | |||
| {\ | |||
| fft##n2(z);\ | |||
| fft##n4(z+n4*2);\ | |||
| fft##n4(z+n4*3);\ | |||
| pass(z,ff_cos_##n,n4/2);\ | |||
| } | |||
| static void fft4(FFTComplex *z) | |||
| { | |||
| FFTSample t1, t2, t3, t4, t5, t6, t7, t8; | |||
| BF(t3, t1, z[0].re, z[1].re); | |||
| BF(t8, t6, z[3].re, z[2].re); | |||
| BF(z[2].re, z[0].re, t1, t6); | |||
| BF(t4, t2, z[0].im, z[1].im); | |||
| BF(t7, t5, z[2].im, z[3].im); | |||
| BF(z[3].im, z[1].im, t4, t8); | |||
| BF(z[3].re, z[1].re, t3, t7); | |||
| BF(z[2].im, z[0].im, t2, t5); | |||
| } | |||
| static void fft8(FFTComplex *z) | |||
| { | |||
| FFTSample t1, t2, t3, t4, t5, t6, t7, t8; | |||
| fft4(z); | |||
| BF(t1, z[5].re, z[4].re, -z[5].re); | |||
| BF(t2, z[5].im, z[4].im, -z[5].im); | |||
| BF(t3, z[7].re, z[6].re, -z[7].re); | |||
| BF(t4, z[7].im, z[6].im, -z[7].im); | |||
| BF(t8, t1, t3, t1); | |||
| BF(t7, t2, t2, t4); | |||
| BF(z[4].re, z[0].re, z[0].re, t1); | |||
| BF(z[4].im, z[0].im, z[0].im, t2); | |||
| BF(z[6].re, z[2].re, z[2].re, t7); | |||
| BF(z[6].im, z[2].im, z[2].im, t8); | |||
| TRANSFORM(z[1],z[3],z[5],z[7],sqrthalf,sqrthalf); | |||
| } | |||
| #ifndef CONFIG_SMALL | |||
| static void fft16(FFTComplex *z) | |||
| { | |||
| FFTSample t1, t2, t3, t4, t5, t6; | |||
| fft8(z); | |||
| fft4(z+8); | |||
| fft4(z+12); | |||
| TRANSFORM_ZERO(z[0],z[4],z[8],z[12]); | |||
| TRANSFORM(z[2],z[6],z[10],z[14],sqrthalf,sqrthalf); | |||
| TRANSFORM(z[1],z[5],z[9],z[13],ff_cos_16[1],ff_cos_16[3]); | |||
| TRANSFORM(z[3],z[7],z[11],z[15],ff_cos_16[3],ff_cos_16[1]); | |||
| } | |||
| #else | |||
| DECL_FFT(16,8,4) | |||
| #endif | |||
| DECL_FFT(32,16,8) | |||
| DECL_FFT(64,32,16) | |||
| DECL_FFT(128,64,32) | |||
| DECL_FFT(256,128,64) | |||
| DECL_FFT(512,256,128) | |||
| #ifndef CONFIG_SMALL | |||
| #define pass pass_big | |||
| #endif | |||
| DECL_FFT(1024,512,256) | |||
| DECL_FFT(2048,1024,512) | |||
| DECL_FFT(4096,2048,1024) | |||
| DECL_FFT(8192,4096,2048) | |||
| DECL_FFT(16384,8192,4096) | |||
| DECL_FFT(32768,16384,8192) | |||
| DECL_FFT(65536,32768,16384) | |||
| static void (*fft_dispatch[])(FFTComplex*) = { | |||
| fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, | |||
| fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, | |||
| }; | |||
| /** | |||
| * Do a complex FFT with the parameters defined in ff_fft_init(). The | |||
| * input data must be permuted before with s->revtab table. No | |||
| * 1.0/sqrt(n) normalization is done. | |||
| */ | |||
| void ff_fft_calc_c(FFTContext *s, FFTComplex *z) | |||
| { | |||
| fft_dispatch[s->nbits-2](z); | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| /* | |||
| * FFT/MDCT transform with 3DNow! optimizations | |||
| * Copyright (c) 2006 Zuxy MENG Jie, Loren Merritt | |||
| * Based on fft_sse.c copyright (c) 2002 Fabrice Bellard. | |||
| * Copyright (c) 2008 Loren Merritt | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| @@ -20,109 +19,5 @@ | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavutil/x86_cpu.h" | |||
| #include "libavcodec/dsputil.h" | |||
| static const int p1m1[2] __attribute__((aligned(8))) = | |||
| { 0, 1 << 31 }; | |||
| static const int m1p1[2] __attribute__((aligned(8))) = | |||
| { 1 << 31, 0 }; | |||
| void ff_fft_calc_3dn(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int ln = s->nbits; | |||
| long j; | |||
| x86_reg i; | |||
| long nblocks, nloops; | |||
| FFTComplex *p, *cptr; | |||
| asm volatile( | |||
| /* FEMMS is not a must here but recommended by AMD */ | |||
| "femms \n\t" | |||
| "movq %0, %%mm7 \n\t" | |||
| ::"m"(*(s->inverse ? m1p1 : p1m1)) | |||
| ); | |||
| i = 8 << ln; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $32, %0 \n\t" | |||
| "movq (%0,%1), %%mm0 \n\t" | |||
| "movq 16(%0,%1), %%mm1 \n\t" | |||
| "movq 8(%0,%1), %%mm2 \n\t" | |||
| "movq 24(%0,%1), %%mm3 \n\t" | |||
| "movq %%mm0, %%mm4 \n\t" | |||
| "movq %%mm1, %%mm5 \n\t" | |||
| "pfadd %%mm2, %%mm0 \n\t" | |||
| "pfadd %%mm3, %%mm1 \n\t" | |||
| "pfsub %%mm2, %%mm4 \n\t" | |||
| "pfsub %%mm3, %%mm5 \n\t" | |||
| "movq %%mm0, %%mm2 \n\t" | |||
| "punpckldq %%mm5, %%mm6 \n\t" | |||
| "punpckhdq %%mm6, %%mm5 \n\t" | |||
| "movq %%mm4, %%mm3 \n\t" | |||
| "pxor %%mm7, %%mm5 \n\t" | |||
| "pfadd %%mm1, %%mm0 \n\t" | |||
| "pfadd %%mm5, %%mm4 \n\t" | |||
| "pfsub %%mm1, %%mm2 \n\t" | |||
| "pfsub %%mm5, %%mm3 \n\t" | |||
| "movq %%mm0, (%0,%1) \n\t" | |||
| "movq %%mm4, 8(%0,%1) \n\t" | |||
| "movq %%mm2, 16(%0,%1) \n\t" | |||
| "movq %%mm3, 24(%0,%1) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(z) | |||
| ); | |||
| /* pass 2 .. ln-1 */ | |||
| nblocks = 1 << (ln-3); | |||
| nloops = 1 << 2; | |||
| cptr = s->exptab1; | |||
| do { | |||
| p = z; | |||
| j = nblocks; | |||
| do { | |||
| i = nloops*8; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $16, %0 \n\t" | |||
| "movq (%1,%0), %%mm0 \n\t" | |||
| "movq 8(%1,%0), %%mm1 \n\t" | |||
| "movq (%2,%0), %%mm2 \n\t" | |||
| "movq 8(%2,%0), %%mm3 \n\t" | |||
| "movq %%mm2, %%mm4 \n\t" | |||
| "movq %%mm3, %%mm5 \n\t" | |||
| "punpckldq %%mm2, %%mm2 \n\t" | |||
| "punpckldq %%mm3, %%mm3 \n\t" | |||
| "punpckhdq %%mm4, %%mm4 \n\t" | |||
| "punpckhdq %%mm5, %%mm5 \n\t" | |||
| "pfmul (%3,%0,2), %%mm2 \n\t" // cre*re cim*re | |||
| "pfmul 8(%3,%0,2), %%mm3 \n\t" | |||
| "pfmul 16(%3,%0,2), %%mm4 \n\t" // -cim*im cre*im | |||
| "pfmul 24(%3,%0,2), %%mm5 \n\t" | |||
| "pfadd %%mm2, %%mm4 \n\t" // cre*re-cim*im cim*re+cre*im | |||
| "pfadd %%mm3, %%mm5 \n\t" | |||
| "movq %%mm0, %%mm2 \n\t" | |||
| "movq %%mm1, %%mm3 \n\t" | |||
| "pfadd %%mm4, %%mm0 \n\t" | |||
| "pfadd %%mm5, %%mm1 \n\t" | |||
| "pfsub %%mm4, %%mm2 \n\t" | |||
| "pfsub %%mm5, %%mm3 \n\t" | |||
| "movq %%mm0, (%1,%0) \n\t" | |||
| "movq %%mm1, 8(%1,%0) \n\t" | |||
| "movq %%mm2, (%2,%0) \n\t" | |||
| "movq %%mm3, 8(%2,%0) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(p), "r"(p + nloops), "r"(cptr) | |||
| ); | |||
| p += nloops*2; | |||
| } while (--j); | |||
| cptr += nloops*2; | |||
| nblocks >>= 1; | |||
| nloops <<= 1; | |||
| } while (nblocks != 0); | |||
| asm volatile("femms"); | |||
| } | |||
| #define EMULATE_3DNOWEXT | |||
| #include "fft_3dn2.c" | |||
| @@ -23,105 +23,26 @@ | |||
| #include "libavutil/x86_cpu.h" | |||
| #include "libavcodec/dsputil.h" | |||
| static const int p1m1[2] __attribute__((aligned(8))) = | |||
| { 0, 1 << 31 }; | |||
| #ifdef EMULATE_3DNOWEXT | |||
| #define ff_fft_calc_3dn2 ff_fft_calc_3dn | |||
| #define ff_fft_dispatch_3dn2 ff_fft_dispatch_3dn | |||
| #define ff_fft_dispatch_interleave_3dn2 ff_fft_dispatch_interleave_3dn | |||
| #define ff_imdct_calc_3dn2 ff_imdct_calc_3dn | |||
| #define ff_imdct_half_3dn2 ff_imdct_half_3dn | |||
| #endif | |||
| static const int m1p1[2] __attribute__((aligned(8))) = | |||
| { 1 << 31, 0 }; | |||
| void ff_fft_dispatch_3dn2(FFTComplex *z, int nbits); | |||
| void ff_fft_dispatch_interleave_3dn2(FFTComplex *z, int nbits); | |||
| void ff_fft_calc_3dn2(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int ln = s->nbits; | |||
| long j; | |||
| x86_reg i; | |||
| long nblocks, nloops; | |||
| FFTComplex *p, *cptr; | |||
| asm volatile( | |||
| /* FEMMS is not a must here but recommended by AMD */ | |||
| "femms \n\t" | |||
| "movq %0, %%mm7 \n\t" | |||
| ::"m"(*(s->inverse ? m1p1 : p1m1)) | |||
| ); | |||
| i = 8 << ln; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $32, %0 \n\t" | |||
| "movq (%0,%1), %%mm0 \n\t" | |||
| "movq 16(%0,%1), %%mm1 \n\t" | |||
| "movq 8(%0,%1), %%mm2 \n\t" | |||
| "movq 24(%0,%1), %%mm3 \n\t" | |||
| "movq %%mm0, %%mm4 \n\t" | |||
| "movq %%mm1, %%mm5 \n\t" | |||
| "pfadd %%mm2, %%mm0 \n\t" | |||
| "pfadd %%mm3, %%mm1 \n\t" | |||
| "pfsub %%mm2, %%mm4 \n\t" | |||
| "pfsub %%mm3, %%mm5 \n\t" | |||
| "movq %%mm0, %%mm2 \n\t" | |||
| "pswapd %%mm5, %%mm5 \n\t" | |||
| "movq %%mm4, %%mm3 \n\t" | |||
| "pxor %%mm7, %%mm5 \n\t" | |||
| "pfadd %%mm1, %%mm0 \n\t" | |||
| "pfadd %%mm5, %%mm4 \n\t" | |||
| "pfsub %%mm1, %%mm2 \n\t" | |||
| "pfsub %%mm5, %%mm3 \n\t" | |||
| "movq %%mm0, (%0,%1) \n\t" | |||
| "movq %%mm4, 8(%0,%1) \n\t" | |||
| "movq %%mm2, 16(%0,%1) \n\t" | |||
| "movq %%mm3, 24(%0,%1) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(z) | |||
| ); | |||
| /* pass 2 .. ln-1 */ | |||
| nblocks = 1 << (ln-3); | |||
| nloops = 1 << 2; | |||
| cptr = s->exptab1; | |||
| do { | |||
| p = z; | |||
| j = nblocks; | |||
| do { | |||
| i = nloops*8; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $16, %0 \n\t" | |||
| "movq (%1,%0), %%mm0 \n\t" | |||
| "movq 8(%1,%0), %%mm1 \n\t" | |||
| "movq (%2,%0), %%mm2 \n\t" | |||
| "movq 8(%2,%0), %%mm3 \n\t" | |||
| "movq (%3,%0,2), %%mm4 \n\t" | |||
| "movq 8(%3,%0,2), %%mm5 \n\t" | |||
| "pswapd %%mm4, %%mm6 \n\t" // no need for cptr[2] & cptr[3] | |||
| "pswapd %%mm5, %%mm7 \n\t" | |||
| "pfmul %%mm2, %%mm4 \n\t" // cre*re cim*im | |||
| "pfmul %%mm3, %%mm5 \n\t" | |||
| "pfmul %%mm2, %%mm6 \n\t" // cim*re cre*im | |||
| "pfmul %%mm3, %%mm7 \n\t" | |||
| "pfpnacc %%mm6, %%mm4 \n\t" // cre*re-cim*im cim*re+cre*im | |||
| "pfpnacc %%mm7, %%mm5 \n\t" | |||
| "movq %%mm0, %%mm2 \n\t" | |||
| "movq %%mm1, %%mm3 \n\t" | |||
| "pfadd %%mm4, %%mm0 \n\t" | |||
| "pfadd %%mm5, %%mm1 \n\t" | |||
| "pfsub %%mm4, %%mm2 \n\t" | |||
| "pfsub %%mm5, %%mm3 \n\t" | |||
| "movq %%mm0, (%1,%0) \n\t" | |||
| "movq %%mm1, 8(%1,%0) \n\t" | |||
| "movq %%mm2, (%2,%0) \n\t" | |||
| "movq %%mm3, 8(%2,%0) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(p), "r"(p + nloops), "r"(cptr) | |||
| ); | |||
| p += nloops*2; | |||
| } while (--j); | |||
| cptr += nloops*2; | |||
| nblocks >>= 1; | |||
| nloops <<= 1; | |||
| } while (nblocks != 0); | |||
| int n = 1<<s->nbits; | |||
| int i; | |||
| ff_fft_dispatch_interleave_3dn2(z, s->nbits); | |||
| asm volatile("femms"); | |||
| if(n <= 8) | |||
| for(i=0; i<n; i+=2) | |||
| FFSWAP(FFTSample, z[i].im, z[i+1].re); | |||
| } | |||
| static void imdct_3dn2(MDCTContext *s, const FFTSample *input, FFTSample *tmp) | |||
| @@ -162,7 +83,7 @@ static void imdct_3dn2(MDCTContext *s, const FFTSample *input, FFTSample *tmp) | |||
| ); | |||
| } | |||
| ff_fft_calc(&s->fft, z); | |||
| ff_fft_calc_3dn2(&s->fft, z); | |||
| /* post rotation + reordering */ | |||
| for(k = 0; k < n4; k++) { | |||
| @@ -0,0 +1,467 @@ | |||
| ;****************************************************************************** | |||
| ;* FFT transform with SSE/3DNow optimizations | |||
| ;* Copyright (c) 2008 Loren Merritt | |||
| ;* | |||
| ;* 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 | |||
| ;* 51, Inc., Foundation Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| ;****************************************************************************** | |||
| ; These functions are not individually interchangeable with the C versions. | |||
| ; While C takes arrays of FFTComplex, SSE/3DNow leave intermediate results | |||
| ; in blocks as conventient to the vector size. | |||
| ; i.e. {4x real, 4x imaginary, 4x real, ...} (or 2x respectively) | |||
| %include "x86inc.asm" | |||
| SECTION_RODATA | |||
| %define M_SQRT1_2 0.70710678118654752440 | |||
| ps_root2: times 4 dd M_SQRT1_2 | |||
| ps_root2mppm: dd -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2 | |||
| ps_m1p1: dd 1<<31, 0 | |||
| %assign i 16 | |||
| %rep 13 | |||
| cextern ff_cos_ %+ i | |||
| %assign i i<<1 | |||
| %endrep | |||
| %ifdef ARCH_X86_64 | |||
| %define pointer dq | |||
| %else | |||
| %define pointer dd | |||
| %endif | |||
| %macro IF0 1+ | |||
| %endmacro | |||
| %macro IF1 1+ | |||
| %1 | |||
| %endmacro | |||
| section .text align=16 | |||
| %macro T2_3DN 4 ; z0, z1, mem0, mem1 | |||
| mova %1, %3 | |||
| mova %2, %1 | |||
| pfadd %1, %4 | |||
| pfsub %2, %4 | |||
| %endmacro | |||
| %macro T4_3DN 6 ; z0, z1, z2, z3, tmp0, tmp1 | |||
| mova %5, %3 | |||
| pfsub %3, %4 | |||
| pfadd %5, %4 ; {t6,t5} | |||
| pxor %3, [ps_m1p1 GLOBAL] ; {t8,t7} | |||
| mova %6, %1 | |||
| pswapd %3, %3 | |||
| pfadd %1, %5 ; {r0,i0} | |||
| pfsub %6, %5 ; {r2,i2} | |||
| mova %4, %2 | |||
| pfadd %2, %3 ; {r1,i1} | |||
| pfsub %4, %3 ; {r3,i3} | |||
| SWAP %3, %6 | |||
| %endmacro | |||
| ; in: %1={r0,i0,r1,i1} %2={r2,i2,r3,i3} | |||
| ; out: %1={r0,r1,r2,r3} %2={i0,i1,i2,i3} | |||
| %macro T4_SSE 3 | |||
| mova %3, %1 | |||
| shufps %1, %2, 0x64 ; {r0,i0,r3,i2} | |||
| shufps %3, %2, 0xce ; {r1,i1,r2,i3} | |||
| mova %2, %1 | |||
| addps %1, %3 ; {t1,t2,t6,t5} | |||
| subps %2, %3 ; {t3,t4,t8,t7} | |||
| mova %3, %1 | |||
| shufps %1, %2, 0x44 ; {t1,t2,t3,t4} | |||
| shufps %3, %2, 0xbe ; {t6,t5,t7,t8} | |||
| mova %2, %1 | |||
| addps %1, %3 ; {r0,i0,r1,i1} | |||
| subps %2, %3 ; {r2,i2,r3,i3} | |||
| mova %3, %1 | |||
| shufps %1, %2, 0x88 ; {r0,r1,r2,r3} | |||
| shufps %3, %2, 0xdd ; {i0,i1,i2,i3} | |||
| SWAP %2, %3 | |||
| %endmacro | |||
| %macro T8_SSE 6 ; r0,i0,r1,i1,t0,t1 | |||
| mova %5, %3 | |||
| shufps %3, %4, 0x44 ; {r4,i4,r6,i6} | |||
| shufps %5, %4, 0xee ; {r5,i5,r7,i7} | |||
| mova %6, %3 | |||
| subps %3, %5 ; {r5,i5,r7,i7} | |||
| addps %6, %5 ; {t1,t2,t3,t4} | |||
| mova %5, %3 | |||
| shufps %5, %5, 0xb1 ; {i5,r5,i7,r7} | |||
| mulps %3, [ps_root2mppm GLOBAL] ; {-r5,i5,r7,-i7} | |||
| mulps %5, [ps_root2 GLOBAL] | |||
| addps %3, %5 ; {t8,t7,ta,t9} | |||
| mova %5, %6 | |||
| shufps %6, %3, 0x36 ; {t3,t2,t9,t8} | |||
| shufps %5, %3, 0x9c ; {t1,t4,t7,ta} | |||
| mova %3, %6 | |||
| addps %6, %5 ; {t1,t2,t9,ta} | |||
| subps %3, %5 ; {t6,t5,tc,tb} | |||
| mova %5, %6 | |||
| shufps %6, %3, 0xd8 ; {t1,t9,t5,tb} | |||
| shufps %5, %3, 0x8d ; {t2,ta,t6,tc} | |||
| mova %3, %1 | |||
| mova %4, %2 | |||
| addps %1, %6 ; {r0,r1,r2,r3} | |||
| addps %2, %5 ; {i0,i1,i2,i3} | |||
| subps %3, %6 ; {r4,r5,r6,r7} | |||
| subps %4, %5 ; {i4,i5,i6,i7} | |||
| %endmacro | |||
| ; scheduled for cpu-bound sizes | |||
| %macro PASS_SMALL 3 ; (to load m4-m7), wre, wim | |||
| IF%1 mova m4, Z(4) | |||
| IF%1 mova m5, Z(5) | |||
| mova m0, %2 ; wre | |||
| mova m2, m4 | |||
| mova m1, %3 ; wim | |||
| mova m3, m5 | |||
| mulps m2, m0 ; r2*wre | |||
| IF%1 mova m6, Z(6) | |||
| mulps m3, m1 ; i2*wim | |||
| IF%1 mova m7, Z(7) | |||
| mulps m4, m1 ; r2*wim | |||
| mulps m5, m0 ; i2*wre | |||
| addps m2, m3 ; r2*wre + i2*wim | |||
| mova m3, m1 | |||
| mulps m1, m6 ; r3*wim | |||
| subps m5, m4 ; i2*wre - r2*wim | |||
| mova m4, m0 | |||
| mulps m3, m7 ; i3*wim | |||
| mulps m4, m6 ; r3*wre | |||
| mulps m0, m7 ; i3*wre | |||
| subps m4, m3 ; r3*wre - i3*wim | |||
| mova m3, Z(0) | |||
| addps m0, m1 ; i3*wre + r3*wim | |||
| mova m1, m4 | |||
| addps m4, m2 ; t5 | |||
| subps m1, m2 ; t3 | |||
| subps m3, m4 ; r2 | |||
| addps m4, Z(0) ; r0 | |||
| mova m6, Z(2) | |||
| mova Z(4), m3 | |||
| mova Z(0), m4 | |||
| mova m3, m5 | |||
| subps m5, m0 ; t4 | |||
| mova m4, m6 | |||
| subps m6, m5 ; r3 | |||
| addps m5, m4 ; r1 | |||
| mova Z(6), m6 | |||
| mova Z(2), m5 | |||
| mova m2, Z(3) | |||
| addps m3, m0 ; t6 | |||
| subps m2, m1 ; i3 | |||
| mova m7, Z(1) | |||
| addps m1, Z(3) ; i1 | |||
| mova Z(7), m2 | |||
| mova Z(3), m1 | |||
| mova m4, m7 | |||
| subps m7, m3 ; i2 | |||
| addps m3, m4 ; i0 | |||
| mova Z(5), m7 | |||
| mova Z(1), m3 | |||
| %endmacro | |||
| ; scheduled to avoid store->load aliasing | |||
| %macro PASS_BIG 1 ; (!interleave) | |||
| mova m4, Z(4) ; r2 | |||
| mova m5, Z(5) ; i2 | |||
| mova m2, m4 | |||
| mova m0, [wq] ; wre | |||
| mova m3, m5 | |||
| mova m1, [wq+o1q] ; wim | |||
| mulps m2, m0 ; r2*wre | |||
| mova m6, Z(6) ; r3 | |||
| mulps m3, m1 ; i2*wim | |||
| mova m7, Z(7) ; i3 | |||
| mulps m4, m1 ; r2*wim | |||
| mulps m5, m0 ; i2*wre | |||
| addps m2, m3 ; r2*wre + i2*wim | |||
| mova m3, m1 | |||
| mulps m1, m6 ; r3*wim | |||
| subps m5, m4 ; i2*wre - r2*wim | |||
| mova m4, m0 | |||
| mulps m3, m7 ; i3*wim | |||
| mulps m4, m6 ; r3*wre | |||
| mulps m0, m7 ; i3*wre | |||
| subps m4, m3 ; r3*wre - i3*wim | |||
| mova m3, Z(0) | |||
| addps m0, m1 ; i3*wre + r3*wim | |||
| mova m1, m4 | |||
| addps m4, m2 ; t5 | |||
| subps m1, m2 ; t3 | |||
| subps m3, m4 ; r2 | |||
| addps m4, Z(0) ; r0 | |||
| mova m6, Z(2) | |||
| mova Z(4), m3 | |||
| mova Z(0), m4 | |||
| mova m3, m5 | |||
| subps m5, m0 ; t4 | |||
| mova m4, m6 | |||
| subps m6, m5 ; r3 | |||
| addps m5, m4 ; r1 | |||
| IF%1 mova Z(6), m6 | |||
| IF%1 mova Z(2), m5 | |||
| mova m2, Z(3) | |||
| addps m3, m0 ; t6 | |||
| subps m2, m1 ; i3 | |||
| mova m7, Z(1) | |||
| addps m1, Z(3) ; i1 | |||
| IF%1 mova Z(7), m2 | |||
| IF%1 mova Z(3), m1 | |||
| mova m4, m7 | |||
| subps m7, m3 ; i2 | |||
| addps m3, m4 ; i0 | |||
| IF%1 mova Z(5), m7 | |||
| IF%1 mova Z(1), m3 | |||
| %if %1==0 | |||
| mova m4, m5 ; r1 | |||
| mova m0, m6 ; r3 | |||
| unpcklps m5, m1 | |||
| unpckhps m4, m1 | |||
| unpcklps m6, m2 | |||
| unpckhps m0, m2 | |||
| mova m1, Z(0) | |||
| mova m2, Z(4) | |||
| mova Z(2), m5 | |||
| mova Z(3), m4 | |||
| mova Z(6), m6 | |||
| mova Z(7), m0 | |||
| mova m5, m1 ; r0 | |||
| mova m4, m2 ; r2 | |||
| unpcklps m1, m3 | |||
| unpckhps m5, m3 | |||
| unpcklps m2, m7 | |||
| unpckhps m4, m7 | |||
| mova Z(0), m1 | |||
| mova Z(1), m5 | |||
| mova Z(4), m2 | |||
| mova Z(5), m4 | |||
| %endif | |||
| %endmacro | |||
| %macro PUNPCK 3 | |||
| mova %3, %1 | |||
| punpckldq %1, %2 | |||
| punpckhdq %3, %2 | |||
| %endmacro | |||
| INIT_XMM | |||
| %define Z(x) [r0+mmsize*x] | |||
| align 16 | |||
| fft4_sse: | |||
| mova m0, Z(0) | |||
| mova m1, Z(1) | |||
| T4_SSE m0, m1, m2 | |||
| mova Z(0), m0 | |||
| mova Z(1), m1 | |||
| ret | |||
| align 16 | |||
| fft8_sse: | |||
| mova m0, Z(0) | |||
| mova m1, Z(1) | |||
| T4_SSE m0, m1, m2 | |||
| mova m2, Z(2) | |||
| mova m3, Z(3) | |||
| T8_SSE m0, m1, m2, m3, m4, m5 | |||
| mova Z(0), m0 | |||
| mova Z(1), m1 | |||
| mova Z(2), m2 | |||
| mova Z(3), m3 | |||
| ret | |||
| align 16 | |||
| fft16_sse: | |||
| mova m0, Z(0) | |||
| mova m1, Z(1) | |||
| T4_SSE m0, m1, m2 | |||
| mova m2, Z(2) | |||
| mova m3, Z(3) | |||
| T8_SSE m0, m1, m2, m3, m4, m5 | |||
| mova m4, Z(4) | |||
| mova m5, Z(5) | |||
| mova Z(0), m0 | |||
| mova Z(1), m1 | |||
| mova Z(2), m2 | |||
| mova Z(3), m3 | |||
| T4_SSE m4, m5, m6 | |||
| mova m6, Z(6) | |||
| mova m7, Z(7) | |||
| T4_SSE m6, m7, m0 | |||
| PASS_SMALL 0, [ff_cos_16 GLOBAL], [ff_cos_16+16 GLOBAL] | |||
| ret | |||
| INIT_MMX | |||
| %macro FFT48_3DN 1 | |||
| align 16 | |||
| fft4%1: | |||
| T2_3DN m0, m1, Z(0), Z(1) | |||
| mova m2, Z(2) | |||
| mova m3, Z(3) | |||
| T4_3DN m0, m1, m2, m3, m4, m5 | |||
| PUNPCK m0, m1, m4 | |||
| PUNPCK m2, m3, m5 | |||
| mova Z(0), m0 | |||
| mova Z(1), m4 | |||
| mova Z(2), m2 | |||
| mova Z(3), m5 | |||
| ret | |||
| align 16 | |||
| fft8%1: | |||
| T2_3DN m0, m1, Z(0), Z(1) | |||
| mova m2, Z(2) | |||
| mova m3, Z(3) | |||
| T4_3DN m0, m1, m2, m3, m4, m5 | |||
| mova Z(0), m0 | |||
| mova Z(2), m2 | |||
| T2_3DN m4, m5, Z(4), Z(5) | |||
| T2_3DN m6, m7, Z(6), Z(7) | |||
| pswapd m0, m5 | |||
| pswapd m2, m7 | |||
| pxor m0, [ps_m1p1 GLOBAL] | |||
| pxor m2, [ps_m1p1 GLOBAL] | |||
| pfsub m5, m0 | |||
| pfadd m7, m2 | |||
| pfmul m5, [ps_root2 GLOBAL] | |||
| pfmul m7, [ps_root2 GLOBAL] | |||
| T4_3DN m1, m3, m5, m7, m0, m2 | |||
| mova Z(5), m5 | |||
| mova Z(7), m7 | |||
| mova m0, Z(0) | |||
| mova m2, Z(2) | |||
| T4_3DN m0, m2, m4, m6, m5, m7 | |||
| PUNPCK m0, m1, m5 | |||
| PUNPCK m2, m3, m7 | |||
| mova Z(0), m0 | |||
| mova Z(1), m5 | |||
| mova Z(2), m2 | |||
| mova Z(3), m7 | |||
| PUNPCK m4, Z(5), m5 | |||
| PUNPCK m6, Z(7), m7 | |||
| mova Z(4), m4 | |||
| mova Z(5), m5 | |||
| mova Z(6), m6 | |||
| mova Z(7), m7 | |||
| ret | |||
| %endmacro | |||
| FFT48_3DN _3dn2 | |||
| %macro pswapd 2 | |||
| %ifidn %1, %2 | |||
| movd [r0+12], %1 | |||
| punpckhdq %1, [r0+8] | |||
| %else | |||
| movq %1, %2 | |||
| psrlq %1, 32 | |||
| punpckldq %1, %2 | |||
| %endif | |||
| %endmacro | |||
| FFT48_3DN _3dn | |||
| %define Z(x) [zq + o1q*(x&6)*((x/6)^1) + o3q*(x/6) + mmsize*(x&1)] | |||
| %macro DECL_PASS 2+ ; name, payload | |||
| align 16 | |||
| %1: | |||
| DEFINE_ARGS z, w, n, o1, o3 | |||
| lea o3q, [nq*3] | |||
| lea o1q, [nq*8] | |||
| shl o3q, 4 | |||
| .loop: | |||
| %2 | |||
| add zq, mmsize*2 | |||
| add wq, mmsize | |||
| sub nd, mmsize/8 | |||
| jg .loop | |||
| rep ret | |||
| %endmacro | |||
| INIT_XMM | |||
| DECL_PASS pass_sse, PASS_BIG 1 | |||
| DECL_PASS pass_interleave_sse, PASS_BIG 0 | |||
| INIT_MMX | |||
| %define mulps pfmul | |||
| %define addps pfadd | |||
| %define subps pfsub | |||
| %define unpcklps punpckldq | |||
| %define unpckhps punpckhdq | |||
| DECL_PASS pass_3dn, PASS_SMALL 1, [wq], [wq+o1q] | |||
| DECL_PASS pass_interleave_3dn, PASS_BIG 0 | |||
| %define pass_3dn2 pass_3dn | |||
| %define pass_interleave_3dn2 pass_interleave_3dn | |||
| %macro DECL_FFT 2-3 ; nbits, cpu, suffix | |||
| %xdefine list_of_fft fft4%2, fft8%2 | |||
| %if %1==5 | |||
| %xdefine list_of_fft list_of_fft, fft16%2 | |||
| %endif | |||
| %assign n 1<<%1 | |||
| %rep 17-%1 | |||
| %assign n2 n/2 | |||
| %assign n4 n/4 | |||
| %xdefine list_of_fft list_of_fft, fft %+ n %+ %3%2 | |||
| align 16 | |||
| fft %+ n %+ %3%2: | |||
| call fft %+ n2 %+ %2 | |||
| add r0, n*4 - (n&(-2<<%1)) | |||
| call fft %+ n4 %+ %2 | |||
| add r0, n*2 - (n2&(-2<<%1)) | |||
| call fft %+ n4 %+ %2 | |||
| sub r0, n*6 + (n2&(-2<<%1)) | |||
| lea r1, [ff_cos_ %+ n GLOBAL] | |||
| mov r2d, n4/2 | |||
| jmp pass%3%2 | |||
| %assign n n*2 | |||
| %endrep | |||
| %undef n | |||
| align 8 | |||
| dispatch_tab%3%2: pointer list_of_fft | |||
| ; On x86_32, this function does the register saving and restoring for all of fft. | |||
| ; The others pass args in registers and don't spill anything. | |||
| cglobal ff_fft_dispatch%3%2, 2,5,0, z, nbits | |||
| lea r2, [dispatch_tab%3%2 GLOBAL] | |||
| mov r2, [r2 + (nbitsq-2)*gprsize] | |||
| call r2 | |||
| RET | |||
| %endmacro ; DECL_FFT | |||
| DECL_FFT 5, _sse | |||
| DECL_FFT 5, _sse, _interleave | |||
| DECL_FFT 4, _3dn | |||
| DECL_FFT 4, _3dn, _interleave | |||
| DECL_FFT 4, _3dn2 | |||
| DECL_FFT 4, _3dn2, _interleave | |||
| @@ -22,124 +22,55 @@ | |||
| #include "libavutil/x86_cpu.h" | |||
| #include "libavcodec/dsputil.h" | |||
| static const int p1p1p1m1[4] __attribute__((aligned(16))) = | |||
| { 0, 0, 0, 1 << 31 }; | |||
| static const int p1p1m1p1[4] __attribute__((aligned(16))) = | |||
| { 0, 0, 1 << 31, 0 }; | |||
| static const int p1p1m1m1[4] __attribute__((aligned(16))) = | |||
| { 0, 0, 1 << 31, 1 << 31 }; | |||
| static const int p1m1p1m1[4] __attribute__((aligned(16))) = | |||
| { 0, 1 << 31, 0, 1 << 31 }; | |||
| static const int m1m1m1m1[4] __attribute__((aligned(16))) = | |||
| { 1 << 31, 1 << 31, 1 << 31, 1 << 31 }; | |||
| #if 0 | |||
| static void print_v4sf(const char *str, __m128 a) | |||
| { | |||
| float *p = (float *)&a; | |||
| printf("%s: %f %f %f %f\n", | |||
| str, p[0], p[1], p[2], p[3]); | |||
| } | |||
| #endif | |||
| void ff_fft_dispatch_sse(FFTComplex *z, int nbits); | |||
| void ff_fft_dispatch_interleave_sse(FFTComplex *z, int nbits); | |||
| /* XXX: handle reverse case */ | |||
| void ff_fft_calc_sse(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int ln = s->nbits; | |||
| x86_reg i; | |||
| long j; | |||
| long nblocks, nloops; | |||
| FFTComplex *p, *cptr; | |||
| int n = 1 << s->nbits; | |||
| asm volatile( | |||
| "movaps %0, %%xmm4 \n\t" | |||
| "movaps %1, %%xmm5 \n\t" | |||
| ::"m"(*p1p1m1m1), | |||
| "m"(*(s->inverse ? p1p1m1p1 : p1p1p1m1)) | |||
| ); | |||
| ff_fft_dispatch_interleave_sse(z, s->nbits); | |||
| i = 8 << ln; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $32, %0 \n\t" | |||
| /* do the pass 0 butterfly */ | |||
| "movaps (%0,%1), %%xmm0 \n\t" | |||
| "movaps %%xmm0, %%xmm1 \n\t" | |||
| "shufps $0x4E, %%xmm0, %%xmm0 \n\t" | |||
| "xorps %%xmm4, %%xmm1 \n\t" | |||
| "addps %%xmm1, %%xmm0 \n\t" | |||
| "movaps 16(%0,%1), %%xmm2 \n\t" | |||
| "movaps %%xmm2, %%xmm3 \n\t" | |||
| "shufps $0x4E, %%xmm2, %%xmm2 \n\t" | |||
| "xorps %%xmm4, %%xmm3 \n\t" | |||
| "addps %%xmm3, %%xmm2 \n\t" | |||
| /* multiply third by -i */ | |||
| /* by toggling the sign bit */ | |||
| "shufps $0xB4, %%xmm2, %%xmm2 \n\t" | |||
| "xorps %%xmm5, %%xmm2 \n\t" | |||
| /* do the pass 1 butterfly */ | |||
| "movaps %%xmm0, %%xmm1 \n\t" | |||
| "addps %%xmm2, %%xmm0 \n\t" | |||
| "subps %%xmm2, %%xmm1 \n\t" | |||
| "movaps %%xmm0, (%0,%1) \n\t" | |||
| "movaps %%xmm1, 16(%0,%1) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(z) | |||
| ); | |||
| /* pass 2 .. ln-1 */ | |||
| if(n <= 16) { | |||
| x86_reg i = -8*n; | |||
| asm volatile( | |||
| "1: \n" | |||
| "movaps (%0,%1), %%xmm0 \n" | |||
| "movaps %%xmm0, %%xmm1 \n" | |||
| "unpcklps 16(%0,%1), %%xmm0 \n" | |||
| "unpckhps 16(%0,%1), %%xmm1 \n" | |||
| "movaps %%xmm0, (%0,%1) \n" | |||
| "movaps %%xmm1, 16(%0,%1) \n" | |||
| "add $32, %0 \n" | |||
| "jl 1b \n" | |||
| :"+r"(i) | |||
| :"r"(z+n) | |||
| :"memory" | |||
| ); | |||
| } | |||
| } | |||
| nblocks = 1 << (ln-3); | |||
| nloops = 1 << 2; | |||
| cptr = s->exptab1; | |||
| do { | |||
| p = z; | |||
| j = nblocks; | |||
| do { | |||
| i = nloops*8; | |||
| asm volatile( | |||
| "1: \n\t" | |||
| "sub $32, %0 \n\t" | |||
| "movaps (%2,%0), %%xmm1 \n\t" | |||
| "movaps (%1,%0), %%xmm0 \n\t" | |||
| "movaps 16(%2,%0), %%xmm5 \n\t" | |||
| "movaps 16(%1,%0), %%xmm4 \n\t" | |||
| "movaps %%xmm1, %%xmm2 \n\t" | |||
| "movaps %%xmm5, %%xmm6 \n\t" | |||
| "shufps $0xA0, %%xmm1, %%xmm1 \n\t" | |||
| "shufps $0xF5, %%xmm2, %%xmm2 \n\t" | |||
| "shufps $0xA0, %%xmm5, %%xmm5 \n\t" | |||
| "shufps $0xF5, %%xmm6, %%xmm6 \n\t" | |||
| "mulps (%3,%0,2), %%xmm1 \n\t" // cre*re cim*re | |||
| "mulps 16(%3,%0,2), %%xmm2 \n\t" // -cim*im cre*im | |||
| "mulps 32(%3,%0,2), %%xmm5 \n\t" // cre*re cim*re | |||
| "mulps 48(%3,%0,2), %%xmm6 \n\t" // -cim*im cre*im | |||
| "addps %%xmm2, %%xmm1 \n\t" | |||
| "addps %%xmm6, %%xmm5 \n\t" | |||
| "movaps %%xmm0, %%xmm3 \n\t" | |||
| "movaps %%xmm4, %%xmm7 \n\t" | |||
| "addps %%xmm1, %%xmm0 \n\t" | |||
| "subps %%xmm1, %%xmm3 \n\t" | |||
| "addps %%xmm5, %%xmm4 \n\t" | |||
| "subps %%xmm5, %%xmm7 \n\t" | |||
| "movaps %%xmm0, (%1,%0) \n\t" | |||
| "movaps %%xmm3, (%2,%0) \n\t" | |||
| "movaps %%xmm4, 16(%1,%0) \n\t" | |||
| "movaps %%xmm7, 16(%2,%0) \n\t" | |||
| "jg 1b \n\t" | |||
| :"+r"(i) | |||
| :"r"(p), "r"(p + nloops), "r"(cptr) | |||
| ); | |||
| p += nloops*2; | |||
| } while (--j); | |||
| cptr += nloops*2; | |||
| nblocks >>= 1; | |||
| nloops <<= 1; | |||
| } while (nblocks != 0); | |||
| void ff_fft_permute_sse(FFTContext *s, FFTComplex *z) | |||
| { | |||
| int n = 1 << s->nbits; | |||
| int i; | |||
| for(i=0; i<n; i+=2) { | |||
| asm volatile( | |||
| "movaps %2, %%xmm0 \n" | |||
| "movlps %%xmm0, %0 \n" | |||
| "movhps %%xmm0, %1 \n" | |||
| :"=m"(s->tmp_buf[s->revtab[i]]), | |||
| "=m"(s->tmp_buf[s->revtab[i+1]]) | |||
| :"m"(z[i]) | |||
| ); | |||
| } | |||
| memcpy(z, s->tmp_buf, n*sizeof(FFTComplex)); | |||
| } | |||
| static void imdct_sse(MDCTContext *s, const FFTSample *input, FFTSample *tmp) | |||