Fix oggplay-dump-first-frame
[liboggplay.git] / src / liboggplay / cpu.c
blob19a2f1601b4a8b8e3e82098e2b2f19df327ec3c9
1 /********************************************************************
2 * *
3 * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
7 * *
8 * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 *
9 * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
10 * *
11 ********************************************************************
13 CPU capability detection for x86 processors.
14 Originally written by Rudolf Marek.
16 function:
17 last mod: $Id$
19 ********************************************************************/
21 #include "cpu.h"
23 /* for detecting AltiVec support */
24 # if (defined(__ppc__) || defined(__ppc64__))
25 # if defined(__APPLE__) || defined(__MACOSX__)
26 #include <sys/sysctl.h>
27 # else
28 #include <signal.h>
29 #include <setjmp.h>
30 # endif
31 # endif
33 # if (defined(__ppc__) || defined(__ppc64__)) && !(defined(__APPLE__) || defined(__MACOSX__))
34 static jmp_buf jmpbuf;
36 static void illegal_instruction(int sig)
38 longjmp(jmpbuf, 1);
40 # endif
42 #if defined(i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64)
43 # if !defined(_MSC_VER)
44 # if defined(__amd64__)||defined(__x86_64__)
45 /*On x86-64, gcc seems to be able to figure out how to save %rbx for us when
46 compiling with -fPIC.*/
47 # define cpuid(_op,_eax,_ebx,_ecx,_edx) \
48 __asm__ __volatile__( \
49 "cpuid\n\t" \
50 :[eax]"=a"(_eax),[ebx]"=b"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \
51 :"a"(_op) \
52 :"cc" \
54 # else
55 /*On x86-32, not so much.*/
56 # define cpuid(_op,_eax,_ebx,_ecx,_edx) \
57 __asm__ __volatile__( \
58 "xchgl %%ebx,%[ebx]\n\t" \
59 "cpuid\n\t" \
60 "xchgl %%ebx,%[ebx]\n\t" \
61 :[eax]"=a"(_eax),[ebx]"=r"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \
62 :"a"(_op) \
63 :"cc" \
65 # endif
66 # else
67 # if defined(_M_IX86)
68 /*Why does MSVC need this complicated rigamarole?
69 At this point I honestly do not care.*/
71 /*Visual C cpuid helper function.
72 For VS2005 we could as well use the _cpuid builtin, but that wouldn't work
73 for VS2003 users, so we do it in inline assembler.*/
74 static void oc_cpuid_helper(ogg_uint32_t _cpu_info[4],ogg_uint32_t _op){
75 _asm {
76 mov eax,[_op]
77 mov esi,_cpu_info
78 cpuid
79 mov [esi+0],eax
80 mov [esi+4],ebx
81 mov [esi+8],ecx
82 mov [esi+12],edx
86 # define cpuid(_op,_eax,_ebx,_ecx,_edx) \
87 do{ \
88 ogg_uint32_t cpu_info[4]; \
89 oc_cpuid_helper(cpu_info,_op); \
90 (_eax)=cpu_info[0]; \
91 (_ebx)=cpu_info[1]; \
92 (_ecx)=cpu_info[2]; \
93 (_edx)=cpu_info[3]; \
94 }while(0)
96 static void oc_detect_cpuid_helper(ogg_uint32_t *_eax,ogg_uint32_t *_ebx){
97 _asm{
98 pushfd
99 pushfd
100 pop eax
101 mov ebx,eax
102 xor eax,200000h
103 push eax
104 popfd
105 pushfd
106 pop eax
107 popfd
108 mov ecx,_eax
109 mov [ecx],eax
110 mov ecx,_ebx
111 mov [ecx],ebx
114 # elif defined(_M_AMD64)
115 # include <intrin.h>
116 # define cpuid(_op,_eax,_ebx,_ecx,_edx) \
117 do{ \
118 int cpu_info[4]; \
119 __cpuid(cpu_info,_op); \
120 (_eax)=cpu_info[0]; \
121 (_ebx)=cpu_info[1]; \
122 (_ecx)=cpu_info[2]; \
123 (_edx)=cpu_info[3]; \
124 }while(0)
125 # endif
126 # endif
127 #endif /* x86-only cpuid */
129 static ogg_uint32_t oc_parse_intel_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){
130 ogg_uint32_t flags;
131 /*If there isn't even MMX, give up.*/
132 if(!(_edx&0x00800000))return 0;
133 flags=OC_CPU_X86_MMX;
134 if(_edx&0x02000000)flags|=OC_CPU_X86_MMXEXT|OC_CPU_X86_SSE;
135 if(_edx&0x04000000)flags|=OC_CPU_X86_SSE2;
136 if(_ecx&0x00000001)flags|=OC_CPU_X86_PNI;
137 if(_ecx&0x00000100)flags|=OC_CPU_X86_SSSE3;
138 if(_ecx&0x00080000)flags|=OC_CPU_X86_SSE4_1;
139 if(_ecx&0x00100000)flags|=OC_CPU_X86_SSE4_2;
140 return flags;
143 static ogg_uint32_t oc_parse_amd_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){
144 ogg_uint32_t flags;
145 /*If there isn't even MMX, give up.*/
146 if(!(_edx&0x00800000))return 0;
147 flags=OC_CPU_X86_MMX;
148 if(_edx&0x00400000)flags|=OC_CPU_X86_MMXEXT;
149 if(_edx&0x80000000)flags|=OC_CPU_X86_3DNOW;
150 if(_edx&0x40000000)flags|=OC_CPU_X86_3DNOWEXT;
151 if(_ecx&0x00000040)flags|=OC_CPU_X86_SSE4A;
152 if(_ecx&0x00000800)flags|=OC_CPU_X86_SSE5;
153 return flags;
156 static ogg_uint32_t oc_cpu_flags_get(void){
157 ogg_uint32_t flags = 0;
158 # if defined(__ppc__) || defined(__ppc64__)
159 /* detect AltiVec extension if compiling it for ppc */
160 # if defined(__APPLE__) || defined(__MACOSX__) || defined(__DARWIN__)
161 int selectors[2] = { CTL_HW, HW_VECTORUNIT };
162 int i_has_altivec = 0;
163 size_t i_length = sizeof( i_has_altivec );
164 int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0);
166 if( i_error == 0 && i_has_altivec != 0 )
167 flags |= OC_CPU_PPC_ALTIVEC;
168 # else
169 void (*handler) (int sig);
170 handler = signal(SIGILL, illegal_instruction);
171 if (setjmp(jmpbuf) == 0)
173 __asm__ __volatile__ (
174 "mtspr 256, %0\n\t"
175 "vand %%v0, %%v0, %%v0"
176 : : "r"(-1) );
178 flags |= OC_CPU_PPC_ALTIVEC;
180 signal(SIGILL, handler);
181 # endif
182 /* detect x86 CPU extensions */
183 # elif defined(i386) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64)
184 ogg_uint32_t eax;
185 ogg_uint32_t ebx;
186 ogg_uint32_t ecx;
187 ogg_uint32_t edx;
188 # if !defined(__amd64__)&&!defined(__x86_64__)&&!defined(_M_AMD64)
189 /*Not all x86-32 chips support cpuid, so we have to check.*/
190 # if !defined(_MSC_VER)
191 __asm__ __volatile__(
192 "pushfl\n\t"
193 "pushfl\n\t"
194 "popl %[a]\n\t"
195 "movl %[a],%[b]\n\t"
196 "xorl $0x200000,%[a]\n\t"
197 "pushl %[a]\n\t"
198 "popfl\n\t"
199 "pushfl\n\t"
200 "popl %[a]\n\t"
201 "popfl\n\t"
202 :[a]"=r"(eax),[b]"=r"(ebx)
204 :"cc"
206 # else
207 oc_detect_cpuid_helper(&eax,&ebx);
208 # endif
209 /*No cpuid.*/
210 if(eax==ebx)return 0;
211 # endif
212 cpuid(0,eax,ebx,ecx,edx);
213 /* l e t n I e n i u n e G*/
214 if((ecx==0x6C65746E&&edx==0x49656E69&&ebx==0x756E6547)||
215 /* 6 8 x M T e n i u n e G*/
216 (ecx==0x3638784D&&edx==0x54656E69&&ebx==0x756E6547)) {
217 /*Intel, Transmeta (tested with Crusoe TM5800):*/
218 cpuid(1,eax,ebx,ecx,edx);
219 flags=oc_parse_intel_flags(edx,ecx);
221 /* D M A c i t n e h t u A*/
222 else if((ecx==0x444D4163&&edx==0x69746E65&&ebx==0x68747541)||
223 /* C S N y b e d o e G*/
224 (ecx==0x43534E20&&edx==0x79622065&&ebx==0x646F6547)){
225 /*AMD, Geode:*/
226 cpuid(0x80000000,eax,ebx,ecx,edx);
227 if(eax<0x80000001)flags=0;
228 else{
229 cpuid(0x80000001,eax,ebx,ecx,edx);
230 flags=oc_parse_amd_flags(edx,ecx);
232 /*Also check for SSE.*/
233 cpuid(1,eax,ebx,ecx,edx);
234 flags|=oc_parse_intel_flags(edx,ecx);
236 /*Technically some VIA chips can be configured in the BIOS to return any
237 string here the user wants.
238 There is a special detection method that can be used to identify such
239 processors, but in my opinion, if the user really wants to change it, they
240 deserve what they get.*/
241 /* s l u a H r u a t n e C*/
242 else if(ecx==0x736C7561&&edx==0x48727561&&ebx==0x746E6543){
243 /*VIA:*/
244 /*I only have documentation for the C7 (Esther) and Isaiah (forthcoming)
245 chips (thanks to the engineers from Centaur Technology who provided it).
246 These chips support Intel-like cpuid info.
247 The C3-2 (Nehemiah) cores appear to, as well.*/
248 cpuid(1,eax,ebx,ecx,edx);
249 flags=oc_parse_intel_flags(edx,ecx);
250 cpuid(0x80000000,eax,ebx,ecx,edx);
251 if(eax>=0x80000001){
252 /*The (non-Nehemiah) C3 processors support AMD-like cpuid info.
253 We need to check this even if the Intel test succeeds to pick up 3DNow!
254 support on these processors.
255 Unlike actual AMD processors, we cannot _rely_ on this info, since
256 some cores (e.g., the 693 stepping of the Nehemiah) claim to support
257 this function, yet return edx=0, despite the Intel test indicating
258 MMX support.
259 Therefore the features detected here are strictly added to those
260 detected by the Intel test.*/
261 /*TODO: How about earlier chips?*/
262 cpuid(0x80000001,eax,ebx,ecx,edx);
263 /*Note: As of the C7, this function returns Intel-style extended feature
264 flags, not AMD-style.
265 Currently, this only defines bits 11, 20, and 29 (0x20100800), which
266 do not conflict with any of the AMD flags we inspect.
267 For the remaining bits, Intel tells us, "Do not count on their value",
268 but VIA assures us that they will all be zero (at least on the C7 and
269 Isaiah chips).
270 In the (unlikely) event a future processor uses bits 18, 19, 30, or 31
271 (0xC0C00000) for something else, we will have to add code to detect
272 the model to decide when it is appropriate to inspect them.*/
273 flags|=oc_parse_amd_flags(edx,ecx);
276 else{
277 /*Implement me.*/
278 flags=0;
280 # else
281 /* not x86 or ppc */
282 # endif
283 return flags;