spec: Use %license macro to copy license
[platform/upstream/libtheora.git] / lib / cpu.c
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-2009                *
9  * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
10  *                                                                  *
11  ********************************************************************
12
13  CPU capability detection for x86 processors.
14   Originally written by Rudolf Marek.
15
16  function:
17   last mod: $Id: cpu.c 16503 2009-08-22 18:14:02Z giles $
18
19  ********************************************************************/
20
21 #include "cpu.h"
22
23 #if !defined(OC_X86_ASM)
24 static ogg_uint32_t oc_cpu_flags_get(void){
25   return 0;
26 }
27 #else
28 # if !defined(_MSC_VER)
29 #  if defined(__amd64__)||defined(__x86_64__)
30 /*On x86-64, gcc seems to be able to figure out how to save %rbx for us when
31    compiling with -fPIC.*/
32 #   define cpuid(_op,_eax,_ebx,_ecx,_edx) \
33   __asm__ __volatile__( \
34    "cpuid\n\t" \
35    :[eax]"=a"(_eax),[ebx]"=b"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \
36    :"a"(_op) \
37    :"cc" \
38   )
39 #  else
40 /*On x86-32, not so much.*/
41 #   define cpuid(_op,_eax,_ebx,_ecx,_edx) \
42   __asm__ __volatile__( \
43    "xchgl %%ebx,%[ebx]\n\t" \
44    "cpuid\n\t" \
45    "xchgl %%ebx,%[ebx]\n\t" \
46    :[eax]"=a"(_eax),[ebx]"=r"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \
47    :"a"(_op) \
48    :"cc" \
49   )
50 #  endif
51 # else
52 /*Why does MSVC need this complicated rigamarole?
53   At this point I honestly do not care.*/
54
55 /*Visual C cpuid helper function.
56   For VS2005 we could as well use the _cpuid builtin, but that wouldn't work
57    for VS2003 users, so we do it in inline assembler.*/
58 static void oc_cpuid_helper(ogg_uint32_t _cpu_info[4],ogg_uint32_t _op){
59   _asm{
60     mov eax,[_op]
61     mov esi,_cpu_info
62     cpuid
63     mov [esi+0],eax
64     mov [esi+4],ebx
65     mov [esi+8],ecx
66     mov [esi+12],edx
67   }
68 }
69
70 #  define cpuid(_op,_eax,_ebx,_ecx,_edx) \
71   do{ \
72     ogg_uint32_t cpu_info[4]; \
73     oc_cpuid_helper(cpu_info,_op); \
74     (_eax)=cpu_info[0]; \
75     (_ebx)=cpu_info[1]; \
76     (_ecx)=cpu_info[2]; \
77     (_edx)=cpu_info[3]; \
78   }while(0)
79
80 static void oc_detect_cpuid_helper(ogg_uint32_t *_eax,ogg_uint32_t *_ebx){
81   _asm{
82     pushfd
83     pushfd
84     pop eax
85     mov ebx,eax
86     xor eax,200000h
87     push eax
88     popfd
89     pushfd
90     pop eax
91     popfd
92     mov ecx,_eax
93     mov [ecx],eax
94     mov ecx,_ebx
95     mov [ecx],ebx
96   }
97 }
98 # endif
99
100 static ogg_uint32_t oc_parse_intel_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){
101   ogg_uint32_t flags;
102   /*If there isn't even MMX, give up.*/
103   if(!(_edx&0x00800000))return 0;
104   flags=OC_CPU_X86_MMX;
105   if(_edx&0x02000000)flags|=OC_CPU_X86_MMXEXT|OC_CPU_X86_SSE;
106   if(_edx&0x04000000)flags|=OC_CPU_X86_SSE2;
107   if(_ecx&0x00000001)flags|=OC_CPU_X86_PNI;
108   if(_ecx&0x00000100)flags|=OC_CPU_X86_SSSE3;
109   if(_ecx&0x00080000)flags|=OC_CPU_X86_SSE4_1;
110   if(_ecx&0x00100000)flags|=OC_CPU_X86_SSE4_2;
111   return flags;
112 }
113
114 static ogg_uint32_t oc_parse_amd_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){
115   ogg_uint32_t flags;
116   /*If there isn't even MMX, give up.*/
117   if(!(_edx&0x00800000))return 0;
118   flags=OC_CPU_X86_MMX;
119   if(_edx&0x00400000)flags|=OC_CPU_X86_MMXEXT;
120   if(_edx&0x80000000)flags|=OC_CPU_X86_3DNOW;
121   if(_edx&0x40000000)flags|=OC_CPU_X86_3DNOWEXT;
122   if(_ecx&0x00000040)flags|=OC_CPU_X86_SSE4A;
123   if(_ecx&0x00000800)flags|=OC_CPU_X86_SSE5;
124   return flags;
125 }
126
127 static ogg_uint32_t oc_cpu_flags_get(void){
128   ogg_uint32_t flags;
129   ogg_uint32_t eax;
130   ogg_uint32_t ebx;
131   ogg_uint32_t ecx;
132   ogg_uint32_t edx;
133 # if !defined(__amd64__)&&!defined(__x86_64__)
134   /*Not all x86-32 chips support cpuid, so we have to check.*/
135 #  if !defined(_MSC_VER)
136   __asm__ __volatile__(
137    "pushfl\n\t"
138    "pushfl\n\t"
139    "popl %[a]\n\t"
140    "movl %[a],%[b]\n\t"
141    "xorl $0x200000,%[a]\n\t"
142    "pushl %[a]\n\t"
143    "popfl\n\t"
144    "pushfl\n\t"
145    "popl %[a]\n\t"
146    "popfl\n\t"
147    :[a]"=r"(eax),[b]"=r"(ebx)
148    :
149    :"cc"
150   );
151 #  else
152   oc_detect_cpuid_helper(&eax,&ebx);
153 #  endif
154   /*No cpuid.*/
155   if(eax==ebx)return 0;
156 # endif
157   cpuid(0,eax,ebx,ecx,edx);
158   /*         l e t n          I e n i          u n e G*/
159   if(ecx==0x6C65746E&&edx==0x49656E69&&ebx==0x756E6547||
160    /*      6 8 x M          T e n i          u n e G*/
161    ecx==0x3638784D&&edx==0x54656E69&&ebx==0x756E6547){
162     /*Intel, Transmeta (tested with Crusoe TM5800):*/
163     cpuid(1,eax,ebx,ecx,edx);
164     flags=oc_parse_intel_flags(edx,ecx);
165   }
166   /*              D M A c          i t n e          h t u A*/
167   else if(ecx==0x444D4163&&edx==0x69746E65&&ebx==0x68747541||
168    /*      C S N            y b   e          d o e G*/
169    ecx==0x43534e20&&edx==0x79622065&&ebx==0x646f6547){
170     /*AMD, Geode:*/
171     cpuid(0x80000000,eax,ebx,ecx,edx);
172     if(eax<0x80000001)flags=0;
173     else{
174       cpuid(0x80000001,eax,ebx,ecx,edx);
175       flags=oc_parse_amd_flags(edx,ecx);
176     }
177     /*Also check for SSE.*/
178     cpuid(1,eax,ebx,ecx,edx);
179     flags|=oc_parse_intel_flags(edx,ecx);
180   }
181   /*Technically some VIA chips can be configured in the BIOS to return any
182      string here the user wants.
183     There is a special detection method that can be used to identify such
184      processors, but in my opinion, if the user really wants to change it, they
185      deserve what they get.*/
186   /*              s l u a          H r u a          t n e C*/
187   else if(ecx==0x736C7561&&edx==0x48727561&&ebx==0x746E6543){
188     /*VIA:*/
189     /*I only have documentation for the C7 (Esther) and Isaiah (forthcoming)
190        chips (thanks to the engineers from Centaur Technology who provided it).
191       These chips support Intel-like cpuid info.
192       The C3-2 (Nehemiah) cores appear to, as well.*/
193     cpuid(1,eax,ebx,ecx,edx);
194     flags=oc_parse_intel_flags(edx,ecx);
195     if(eax>=0x80000001){
196       /*The (non-Nehemiah) C3 processors support AMD-like cpuid info.
197         We need to check this even if the Intel test succeeds to pick up 3DNow!
198          support on these processors.
199         Unlike actual AMD processors, we cannot _rely_ on this info, since
200          some cores (e.g., the 693 stepping of the Nehemiah) claim to support
201          this function, yet return edx=0, despite the Intel test indicating
202          MMX support.
203         Therefore the features detected here are strictly added to those
204          detected by the Intel test.*/
205       /*TODO: How about earlier chips?*/
206       cpuid(0x80000001,eax,ebx,ecx,edx);
207       /*Note: As of the C7, this function returns Intel-style extended feature
208          flags, not AMD-style.
209         Currently, this only defines bits 11, 20, and 29 (0x20100800), which
210          do not conflict with any of the AMD flags we inspect.
211         For the remaining bits, Intel tells us, "Do not count on their value",
212          but VIA assures us that they will all be zero (at least on the C7 and
213          Isaiah chips).
214         In the (unlikely) event a future processor uses bits 18, 19, 30, or 31
215          (0xC0C00000) for something else, we will have to add code to detect
216          the model to decide when it is appropriate to inspect them.*/
217       flags|=oc_parse_amd_flags(edx,ecx);
218     }
219   }
220   else{
221     /*Implement me.*/
222     flags=0;
223   }
224   return flags;
225 }
226 #endif