9636f78ba3
Initial support for the AMD amdfam10 chipsets has been available in the gcc43 branch under GPLv2. AMD and some linux distributions (OpenSUSE) did a backport of the amdfam10 support and made it available. This is a revised subset of the support initially brought in in r236962 and later reverted. The collateral efects seem to have disappeared but it is still recommended to set the CPUTYPE with caution. Reviewed by: jkim (ages ago) MFC after: 3 weeks
315 lines
7.1 KiB
C
315 lines
7.1 KiB
C
/* Subroutines for the gcc driver.
|
|
Copyright (C) 2006, 2007 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);
|
|
|
|
#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_SSSE3 (1 << 9)
|
|
#define bit_SSE4a (1 << 6)
|
|
#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_ssse3 = 0, has_cmov = 0;
|
|
unsigned char has_longmode = 0, has_cmpxchg8b = 0, has_sse4a = 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);
|
|
has_ssse3 = !!(ecx & bit_SSSE3);
|
|
/* 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);
|
|
has_sse4a = !!(ecx & bit_SSE4a);
|
|
}
|
|
|
|
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;
|
|
if (has_sse4a)
|
|
processor = PROCESSOR_AMDFAM10;
|
|
}
|
|
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_ssse3)
|
|
cpu = "core2";
|
|
else 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_GEODE:
|
|
cpu = "geode";
|
|
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_AMDFAM10:
|
|
cpu = "amdfam10";
|
|
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 */
|