]>
Commit | Line | Data |
---|---|---|
fa959ce4 MM |
1 | /* Subroutines for the gcc driver. |
2 | Copyright (C) 2006 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2, or (at your option) | |
9 | any later version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GCC; see the file COPYING. If not, write to | |
18 | the Free Software Foundation, 51 Franklin Street, Fifth Floor, | |
19 | Boston, MA 02110-1301, USA. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "system.h" | |
23 | #include <stdlib.h> | |
24 | ||
fa959ce4 MM |
25 | #ifdef GCC_VERSION |
26 | #define cpuid(num,a,b,c,d) \ | |
27 | asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ | |
28 | : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ | |
29 | : "0" (num)) | |
30 | ||
31 | #define bit_CMPXCHG8B (1 << 8) | |
32 | #define bit_CMOV (1 << 15) | |
33 | #define bit_MMX (1 << 23) | |
34 | #define bit_SSE (1 << 25) | |
35 | #define bit_SSE2 (1 << 26) | |
36 | ||
37 | #define bit_SSE3 (1 << 0) | |
38 | #define bit_CMPXCHG16B (1 << 13) | |
39 | ||
40 | #define bit_3DNOW (1 << 31) | |
41 | #define bit_3DNOWP (1 << 30) | |
42 | #define bit_LM (1 << 29) | |
43 | ||
44 | /* This will be called by the spec parser in gcc.c when it sees | |
45 | a %:local_cpu_detect(args) construct. Currently it will be called | |
46 | with either "arch" or "tune" as argument depending on if -march=native | |
47 | or -mtune=native is to be substituted. | |
48 | ||
49 | It returns a string containing new command line parameters to be | |
50 | put at the place of the above two options, depending on what CPU | |
51 | this is executed. E.g. "-march=k8" on an AMD64 machine | |
52 | for -march=native. | |
53 | ||
54 | ARGC and ARGV are set depending on the actual arguments given | |
55 | in the spec. */ | |
56 | const char *host_detect_local_cpu (int argc, const char **argv) | |
57 | { | |
58 | const char *cpu = "i386"; | |
59 | unsigned int eax, ebx, ecx, edx; | |
60 | unsigned int max_level; | |
61 | unsigned int vendor; | |
62 | unsigned int ext_level; | |
63 | unsigned char has_mmx = 0, has_3dnow = 0, has_3dnowp = 0, has_sse = 0; | |
64 | unsigned char has_sse2 = 0, has_sse3 = 0, has_cmov = 0; | |
65 | unsigned char has_longmode = 0; | |
66 | unsigned char is_amd = 0; | |
67 | unsigned int family = 0; | |
68 | if (argc < 1 | |
69 | || (strcmp (argv[0], "arch") | |
70 | && strcmp (argv[0], "tune"))) | |
71 | return NULL; | |
72 | ||
73 | #ifndef __x86_64__ | |
74 | /* See if we can use cpuid. */ | |
75 | asm volatile ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" | |
76 | "pushl %0; popfl; pushfl; popl %0; popfl" | |
77 | : "=&r" (eax), "=&r" (ebx) | |
78 | : "i" (0x00200000)); | |
79 | ||
80 | if (((eax ^ ebx) & 0x00200000) == 0) | |
81 | goto done; | |
82 | #endif | |
83 | ||
84 | cpu = "i586"; | |
85 | ||
86 | /* Check the highest input value for eax. */ | |
87 | cpuid (0, eax, ebx, ecx, edx); | |
88 | max_level = eax; | |
89 | /* We only look at the first four characters. */ | |
90 | vendor = ebx; | |
91 | if (max_level == 0) | |
92 | goto done; | |
93 | ||
94 | cpuid (1, eax, ebx, ecx, edx); | |
95 | has_cmov = !!(edx & bit_CMOV); | |
96 | has_mmx = !!(edx & bit_MMX); | |
97 | has_sse = !!(edx & bit_SSE); | |
98 | has_sse2 = !!(edx & bit_SSE2); | |
99 | has_sse3 = !!(ecx & bit_SSE3); | |
100 | /* We don't care for extended family. */ | |
101 | family = (eax >> 8) & ~(1 << 4); | |
102 | ||
103 | cpuid (0x80000000, eax, ebx, ecx, edx); | |
104 | ext_level = eax; | |
105 | if (ext_level >= 0x80000000) | |
106 | { | |
107 | cpuid (0x80000001, eax, ebx, ecx, edx); | |
108 | has_3dnow = !!(edx & bit_3DNOW); | |
109 | has_3dnowp = !!(edx & bit_3DNOWP); | |
110 | has_longmode = !!(edx & bit_LM); | |
111 | } | |
112 | ||
113 | is_amd = vendor == *(unsigned int*)"Auth"; | |
114 | ||
115 | if (is_amd) | |
116 | { | |
117 | if (has_mmx) | |
118 | cpu = "k6"; | |
119 | if (has_3dnow) | |
120 | cpu = "k6-3"; | |
121 | if (has_3dnowp) | |
122 | cpu = "athlon"; | |
123 | if (has_sse) | |
124 | cpu = "athlon-4"; | |
125 | if (has_sse2 || has_longmode) | |
126 | cpu = "k8"; | |
127 | } | |
128 | else | |
129 | { | |
130 | if (family == 5) | |
131 | { | |
132 | if (has_mmx) | |
133 | cpu = "pentium-mmx"; | |
134 | } | |
135 | else if (has_mmx) | |
136 | cpu = "pentium2"; | |
137 | if (has_sse) | |
138 | cpu = "pentium3"; | |
139 | if (has_sse2) | |
140 | { | |
141 | if (family == 6) | |
142 | /* It's a pentiumpro with sse2 --> pentium-m */ | |
143 | cpu = "pentium-m"; | |
144 | else | |
145 | /* Would have to look at extended family, but it's at least | |
146 | an pentium4 core. */ | |
147 | cpu = "pentium4"; | |
148 | } | |
149 | if (has_sse3) | |
150 | { | |
151 | if (has_longmode) | |
152 | cpu = "nocona"; | |
153 | else | |
154 | cpu = "prescott"; | |
155 | } | |
156 | } | |
157 | ||
158 | done: | |
159 | return concat ("-m", argv[0], "=", cpu, NULL); | |
160 | } | |
161 | #else | |
162 | /* If we aren't compiling with GCC we just provide a minimal | |
163 | default value. */ | |
164 | const char *host_detect_local_cpu (int argc, const char **argv) | |
165 | { | |
166 | return concat ("-m", argv[0], "=i386", NULL); | |
167 | } | |
682cd442 | 168 | #endif /* GCC_VERSION */ |