fmtconvert: port int32_to_float_fmul_scalar() x86 inline asm to yasm
[ffmpeg.git] / libavcodec / x86 / fmtconvert_mmx.c
1 /*
2  * Format Conversion Utils
3  * Copyright (c) 2000, 2001 Fabrice Bellard
4  * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
5  *
6  * This file is part of Libav.
7  *
8  * Libav is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * Libav is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with Libav; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  *
22  * MMX optimization by Nick Kurshev <nickols_k@mail.ru>
23  */
24
25 #include "libavutil/cpu.h"
26 #include "libavutil/x86_cpu.h"
27 #include "libavcodec/fmtconvert.h"
28
29 #if HAVE_YASM
30
31 void ff_int32_to_float_fmul_scalar_sse (float *dst, const int *src, float mul, int len);
32 void ff_int32_to_float_fmul_scalar_sse2(float *dst, const int *src, float mul, int len);
33
34 void ff_float_to_int16_3dnow(int16_t *dst, const float *src, long len);
35 void ff_float_to_int16_sse  (int16_t *dst, const float *src, long len);
36 void ff_float_to_int16_sse2 (int16_t *dst, const float *src, long len);
37
38 void ff_float_to_int16_interleave6_sse(int16_t *dst, const float **src, int len);
39 void ff_float_to_int16_interleave6_3dnow(int16_t *dst, const float **src, int len);
40 void ff_float_to_int16_interleave6_3dn2(int16_t *dst, const float **src, int len);
41
42 #define ff_float_to_int16_interleave6_sse2 ff_float_to_int16_interleave6_sse
43
44 #define FLOAT_TO_INT16_INTERLEAVE(cpu, body) \
45 /* gcc pessimizes register allocation if this is in the same function as float_to_int16_interleave_sse2*/\
46 static av_noinline void float_to_int16_interleave_misc_##cpu(int16_t *dst, const float **src, long len, int channels){\
47     DECLARE_ALIGNED(16, int16_t, tmp)[len];\
48     int i,j,c;\
49     for(c=0; c<channels; c++){\
50         ff_float_to_int16_##cpu(tmp, src[c], len);\
51         for(i=0, j=c; i<len; i++, j+=channels)\
52             dst[j] = tmp[i];\
53     }\
54 }\
55 \
56 static void float_to_int16_interleave_##cpu(int16_t *dst, const float **src, long len, int channels){\
57     if(channels==1)\
58         ff_float_to_int16_##cpu(dst, src[0], len);\
59     else if(channels==2){\
60         x86_reg reglen = len; \
61         const float *src0 = src[0];\
62         const float *src1 = src[1];\
63         __asm__ volatile(\
64             "shl $2, %0 \n"\
65             "add %0, %1 \n"\
66             "add %0, %2 \n"\
67             "add %0, %3 \n"\
68             "neg %0 \n"\
69             body\
70             :"+r"(reglen), "+r"(dst), "+r"(src0), "+r"(src1)\
71         );\
72     }else if(channels==6){\
73         ff_float_to_int16_interleave6_##cpu(dst, src, len);\
74     }else\
75         float_to_int16_interleave_misc_##cpu(dst, src, len, channels);\
76 }
77
78 FLOAT_TO_INT16_INTERLEAVE(3dnow,
79     "1:                         \n"
80     "pf2id     (%2,%0), %%mm0   \n"
81     "pf2id    8(%2,%0), %%mm1   \n"
82     "pf2id     (%3,%0), %%mm2   \n"
83     "pf2id    8(%3,%0), %%mm3   \n"
84     "packssdw    %%mm1, %%mm0   \n"
85     "packssdw    %%mm3, %%mm2   \n"
86     "movq        %%mm0, %%mm1   \n"
87     "punpcklwd   %%mm2, %%mm0   \n"
88     "punpckhwd   %%mm2, %%mm1   \n"
89     "movq        %%mm0,  (%1,%0)\n"
90     "movq        %%mm1, 8(%1,%0)\n"
91     "add $16, %0                \n"
92     "js 1b                      \n"
93     "femms                      \n"
94 )
95
96 FLOAT_TO_INT16_INTERLEAVE(sse,
97     "1:                         \n"
98     "cvtps2pi  (%2,%0), %%mm0   \n"
99     "cvtps2pi 8(%2,%0), %%mm1   \n"
100     "cvtps2pi  (%3,%0), %%mm2   \n"
101     "cvtps2pi 8(%3,%0), %%mm3   \n"
102     "packssdw    %%mm1, %%mm0   \n"
103     "packssdw    %%mm3, %%mm2   \n"
104     "movq        %%mm0, %%mm1   \n"
105     "punpcklwd   %%mm2, %%mm0   \n"
106     "punpckhwd   %%mm2, %%mm1   \n"
107     "movq        %%mm0,  (%1,%0)\n"
108     "movq        %%mm1, 8(%1,%0)\n"
109     "add $16, %0                \n"
110     "js 1b                      \n"
111     "emms                       \n"
112 )
113
114 FLOAT_TO_INT16_INTERLEAVE(sse2,
115     "1:                         \n"
116     "cvtps2dq  (%2,%0), %%xmm0  \n"
117     "cvtps2dq  (%3,%0), %%xmm1  \n"
118     "packssdw   %%xmm1, %%xmm0  \n"
119     "movhlps    %%xmm0, %%xmm1  \n"
120     "punpcklwd  %%xmm1, %%xmm0  \n"
121     "movdqa     %%xmm0, (%1,%0) \n"
122     "add $16, %0                \n"
123     "js 1b                      \n"
124 )
125
126 static void float_to_int16_interleave_3dn2(int16_t *dst, const float **src, long len, int channels){
127     if(channels==6)
128         ff_float_to_int16_interleave6_3dn2(dst, src, len);
129     else
130         float_to_int16_interleave_3dnow(dst, src, len, channels);
131 }
132
133 void ff_float_interleave2_mmx(float *dst, const float **src, unsigned int len);
134 void ff_float_interleave2_sse(float *dst, const float **src, unsigned int len);
135
136 void ff_float_interleave6_mmx(float *dst, const float **src, unsigned int len);
137 void ff_float_interleave6_sse(float *dst, const float **src, unsigned int len);
138
139 static void float_interleave_mmx(float *dst, const float **src,
140                                  unsigned int len, int channels)
141 {
142     if (channels == 2) {
143         ff_float_interleave2_mmx(dst, src, len);
144     } else if (channels == 6)
145         ff_float_interleave6_mmx(dst, src, len);
146     else
147         ff_float_interleave_c(dst, src, len, channels);
148 }
149
150 static void float_interleave_sse(float *dst, const float **src,
151                                  unsigned int len, int channels)
152 {
153     if (channels == 2) {
154         ff_float_interleave2_sse(dst, src, len);
155     } else if (channels == 6)
156         ff_float_interleave6_sse(dst, src, len);
157     else
158         ff_float_interleave_c(dst, src, len, channels);
159 }
160 #endif
161
162 void ff_fmt_convert_init_x86(FmtConvertContext *c, AVCodecContext *avctx)
163 {
164     int mm_flags = av_get_cpu_flags();
165
166 #if HAVE_YASM
167     if (mm_flags & AV_CPU_FLAG_MMX) {
168         c->float_interleave = float_interleave_mmx;
169
170         if (HAVE_AMD3DNOW && mm_flags & AV_CPU_FLAG_3DNOW) {
171             if(!(avctx->flags & CODEC_FLAG_BITEXACT)){
172                 c->float_to_int16 = ff_float_to_int16_3dnow;
173                 c->float_to_int16_interleave = float_to_int16_interleave_3dnow;
174             }
175         }
176         if (HAVE_AMD3DNOWEXT && mm_flags & AV_CPU_FLAG_3DNOWEXT) {
177             if(!(avctx->flags & CODEC_FLAG_BITEXACT)){
178                 c->float_to_int16_interleave = float_to_int16_interleave_3dn2;
179             }
180         }
181         if (HAVE_SSE && mm_flags & AV_CPU_FLAG_SSE) {
182             c->int32_to_float_fmul_scalar = ff_int32_to_float_fmul_scalar_sse;
183             c->float_to_int16 = ff_float_to_int16_sse;
184             c->float_to_int16_interleave = float_to_int16_interleave_sse;
185             c->float_interleave = float_interleave_sse;
186         }
187         if (HAVE_SSE && mm_flags & AV_CPU_FLAG_SSE2) {
188             c->int32_to_float_fmul_scalar = ff_int32_to_float_fmul_scalar_sse2;
189             c->float_to_int16 = ff_float_to_int16_sse2;
190             c->float_to_int16_interleave = float_to_int16_interleave_sse2;
191         }
192     }
193 #endif
194 }