| /* Subroutines for the gcc driver. |
| Copyright (C) 2006 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GCC 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING. If not, write to |
| the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include <stdlib.h> |
| |
| const char *host_detect_local_cpu (int argc, const char **argv); |
| |
| /* LLVM LOCAL fix bootstrap failure on ppc host */ |
| #if defined(__i386__) || defined(__x86_64__) |
| #ifdef GCC_VERSION |
| #define cpuid(num,a,b,c,d) \ |
| asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ |
| : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ |
| : "0" (num)) |
| |
| #define bit_CMPXCHG8B (1 << 8) |
| #define bit_CMOV (1 << 15) |
| #define bit_MMX (1 << 23) |
| #define bit_SSE (1 << 25) |
| #define bit_SSE2 (1 << 26) |
| |
| #define bit_SSE3 (1 << 0) |
| #define bit_CMPXCHG16B (1 << 13) |
| |
| #define bit_3DNOW (1 << 31) |
| #define bit_3DNOWP (1 << 30) |
| #define bit_LM (1 << 29) |
| |
| /* This will be called by the spec parser in gcc.c when it sees |
| a %:local_cpu_detect(args) construct. Currently it will be called |
| with either "arch" or "tune" as argument depending on if -march=native |
| or -mtune=native is to be substituted. |
| |
| It returns a string containing new command line parameters to be |
| put at the place of the above two options, depending on what CPU |
| this is executed. E.g. "-march=k8" on an AMD64 machine |
| for -march=native. |
| |
| ARGC and ARGV are set depending on the actual arguments given |
| in the spec. */ |
| const char *host_detect_local_cpu (int argc, const char **argv) |
| { |
| const char *cpu = NULL; |
| enum processor_type processor = PROCESSOR_I386; |
| unsigned int eax, ebx, ecx, edx; |
| unsigned int max_level; |
| unsigned int vendor; |
| unsigned int ext_level; |
| unsigned char has_mmx = 0, has_3dnow = 0, has_3dnowp = 0, has_sse = 0; |
| unsigned char has_sse2 = 0, has_sse3 = 0, has_cmov = 0; |
| unsigned char has_longmode = 0, has_cmpxchg8b = 0; |
| unsigned char is_amd = 0; |
| unsigned int family = 0; |
| bool arch; |
| |
| if (argc < 1) |
| return NULL; |
| |
| arch = strcmp (argv[0], "arch") == 0; |
| if (!arch && strcmp (argv[0], "tune")) |
| return NULL; |
| |
| #ifndef __x86_64__ |
| /* See if we can use cpuid. */ |
| asm volatile ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" |
| "pushl %0; popfl; pushfl; popl %0; popfl" |
| : "=&r" (eax), "=&r" (ebx) |
| : "i" (0x00200000)); |
| |
| if (((eax ^ ebx) & 0x00200000) == 0) |
| goto done; |
| #endif |
| |
| processor = PROCESSOR_PENTIUM; |
| |
| /* Check the highest input value for eax. */ |
| cpuid (0, eax, ebx, ecx, edx); |
| max_level = eax; |
| /* We only look at the first four characters. */ |
| vendor = ebx; |
| if (max_level == 0) |
| goto done; |
| |
| cpuid (1, eax, ebx, ecx, edx); |
| has_cmpxchg8b = !!(edx & bit_CMPXCHG8B); |
| has_cmov = !!(edx & bit_CMOV); |
| has_mmx = !!(edx & bit_MMX); |
| has_sse = !!(edx & bit_SSE); |
| has_sse2 = !!(edx & bit_SSE2); |
| has_sse3 = !!(ecx & bit_SSE3); |
| /* We don't care for extended family. */ |
| family = (eax >> 8) & ~(1 << 4); |
| |
| cpuid (0x80000000, eax, ebx, ecx, edx); |
| ext_level = eax; |
| if (ext_level >= 0x80000000) |
| { |
| cpuid (0x80000001, eax, ebx, ecx, edx); |
| has_3dnow = !!(edx & bit_3DNOW); |
| has_3dnowp = !!(edx & bit_3DNOWP); |
| has_longmode = !!(edx & bit_LM); |
| } |
| |
| is_amd = vendor == *(unsigned int*)"Auth"; |
| |
| if (is_amd) |
| { |
| if (has_mmx) |
| processor = PROCESSOR_K6; |
| if (has_3dnowp) |
| processor = PROCESSOR_ATHLON; |
| if (has_sse2 || has_longmode) |
| processor = PROCESSOR_K8; |
| } |
| else |
| { |
| switch (family) |
| { |
| case 5: |
| /* Default is PROCESSOR_PENTIUM. */ |
| break; |
| case 6: |
| processor = PROCESSOR_PENTIUMPRO; |
| break; |
| case 15: |
| processor = PROCESSOR_PENTIUM4; |
| break; |
| default: |
| /* We have no idea. Use something reasonable. */ |
| if (arch) |
| { |
| if (has_sse3) |
| { |
| if (has_longmode) |
| cpu = "nocona"; |
| else |
| cpu = "prescott"; |
| } |
| else if (has_sse2) |
| cpu = "pentium4"; |
| else if (has_cmov) |
| cpu = "pentiumpro"; |
| else if (has_mmx) |
| cpu = "pentium-mmx"; |
| else if (has_cmpxchg8b) |
| cpu = "pentium"; |
| else |
| cpu = "i386"; |
| } |
| else |
| cpu = "generic"; |
| goto done; |
| break; |
| } |
| } |
| |
| switch (processor) |
| { |
| case PROCESSOR_I386: |
| cpu = "i386"; |
| break; |
| case PROCESSOR_I486: |
| cpu = "i486"; |
| break; |
| case PROCESSOR_PENTIUM: |
| if (has_mmx) |
| cpu = "pentium-mmx"; |
| else |
| cpu = "pentium"; |
| break; |
| case PROCESSOR_PENTIUMPRO: |
| if (arch) |
| { |
| if (has_sse3) |
| { |
| if (has_longmode) |
| { |
| /* It is Core 2 Duo. */ |
| cpu = "nocona"; |
| } |
| else |
| { |
| /* It is Core Duo. */ |
| cpu = "prescott"; |
| } |
| } |
| else if (has_sse2) |
| { |
| /* It is Pentium M. */ |
| cpu = "pentium4"; |
| } |
| else if (has_sse) |
| { |
| /* It is Pentium III. */ |
| cpu = "pentium3"; |
| } |
| else if (has_mmx) |
| { |
| /* It is Pentium II. */ |
| cpu = "pentium2"; |
| } |
| else |
| { |
| /* Default to Pentium Pro. */ |
| cpu = "pentiumpro"; |
| } |
| } |
| else |
| { |
| /* For -mtune, we default to -mtune=generic. */ |
| cpu = "generic"; |
| } |
| break; |
| case PROCESSOR_K6: |
| if (has_3dnow) |
| cpu = "k6-3"; |
| else |
| cpu = "k6"; |
| break; |
| case PROCESSOR_ATHLON: |
| if (has_sse) |
| cpu = "athlon-4"; |
| else |
| cpu = "athlon"; |
| break; |
| case PROCESSOR_PENTIUM4: |
| if (has_sse3) |
| { |
| if (has_longmode) |
| cpu = "nocona"; |
| else |
| cpu = "prescott"; |
| } |
| else |
| cpu = "pentium4"; |
| break; |
| case PROCESSOR_K8: |
| cpu = "k8"; |
| break; |
| case PROCESSOR_NOCONA: |
| cpu = "nocona"; |
| break; |
| case PROCESSOR_GENERIC32: |
| case PROCESSOR_GENERIC64: |
| cpu = "generic"; |
| break; |
| default: |
| abort (); |
| break; |
| } |
| |
| done: |
| return concat ("-m", argv[0], "=", cpu, NULL); |
| } |
| #else |
| /* If we aren't compiling with GCC we just provide a minimal |
| default value. */ |
| const char *host_detect_local_cpu (int argc, const char **argv) |
| { |
| const char *cpu; |
| bool arch; |
| |
| if (argc < 1) |
| return NULL; |
| |
| arch = strcmp (argv[0], "arch") == 0; |
| if (!arch && strcmp (argv[0], "tune")) |
| return NULL; |
| |
| if (arch) |
| { |
| /* FIXME: i386 is wrong for 64bit compiler. How can we tell if |
| we are generating 64bit or 32bit code? */ |
| cpu = "i386"; |
| } |
| else |
| cpu = "generic"; |
| |
| return concat ("-m", argv[0], "=", cpu, NULL); |
| } |
| #endif /* GCC_VERSION */ |
| /* LLVM LOCAL fix bootstrap failure on ppc host */ |
| #endif |
| |