diff options
author | Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> | 2012-05-10 08:51:39 +0000 |
---|---|---|
committer | Rainer Orth <ro@gcc.gnu.org> | 2012-05-10 08:51:39 +0000 |
commit | aca06c900e84dcbd57eaee3e94011a1710ef1e4e (patch) | |
tree | 7538481d3fc7b9aee5791e44a2b759eea9dacb14 /libgcc/config/i386/cpuinfo.c | |
parent | 638d20652bd82d36495fc024a5a618dfd349969e (diff) | |
download | gcc-aca06c900e84dcbd57eaee3e94011a1710ef1e4e.zip gcc-aca06c900e84dcbd57eaee3e94011a1710ef1e4e.tar.gz gcc-aca06c900e84dcbd57eaee3e94011a1710ef1e4e.tar.bz2 |
Use i386-cpuinfo.c on all i386 targets
libgcc:
* config.host (i[34567]86-*-linux*, x86_64-*-linux*)
(i[34567]86-*-kfreebsd*-gnu, x86_64-*-kfreebsd*-gnu)
(i[34567]86-*-knetbsd*-gnu, i[34567]86-*-gnu*): Move
i386/t-cpuinfo ...
(i[34567]86-*-*, x86_64-*-*): ... here.
* config/i386/libgcc-bsd.ver (GCC_4.8.0): New version.
* config/i386/libgcc-sol2.ver (GCC_4.8.0): New version.
* config/i386/i386-cpuinfo.c: Rename to ...
* config/i386/cpuinfo.c: ... this.
* config/i386/t-cpuinfo (LIB2ADD): Reflect this.
* configure.ac (AC_CONFIG_HEADER): Call for auto-target.h.
(libgcc_cv_init_priority): New test.
* configure: Regenerate.
* config.in: New file.
* Makefile.in (clean): Rename config.h to auto-target.h.
(config.h): Likewise.
(stamp-h): Likewise.
* config/i386/cpuinfo.c (auto-target.h): Include.
(CONSTRUCTOR_PRIORITY): Define.
(__cpu_indicator_init): Use it.
gcc
* doc/extend.texi (X86 Built-in Functions, __builtin_cpu_init):
Document requirement to call in constructors.
* config/i386/i386.c: Update comments for i386-cpuinfo.c name
change.
From-SVN: r187365
Diffstat (limited to 'libgcc/config/i386/cpuinfo.c')
-rw-r--r-- | libgcc/config/i386/cpuinfo.c | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/libgcc/config/i386/cpuinfo.c b/libgcc/config/i386/cpuinfo.c new file mode 100644 index 0000000..1c22d7a --- /dev/null +++ b/libgcc/config/i386/cpuinfo.c @@ -0,0 +1,346 @@ +/* Get CPU type and Features for x86 processors. + Copyright (C) 2012 Free Software Foundation, Inc. + Contributed by Sriraman Tallam (tmsriram@google.com) + +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 3, 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 COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "cpuid.h" +#include "tsystem.h" +#include "auto-target.h" + +#ifdef HAVE_INIT_PRIORITY +#define CONSTRUCTOR_PRIORITY (101) +#else +#define CONSTRUCTOR_PRIORITY +#endif + +int __cpu_indicator_init (void) + __attribute__ ((constructor CONSTRUCTOR_PRIORITY)); + +enum vendor_signatures +{ + SIG_INTEL = 0x756e6547 /* Genu */, + SIG_AMD = 0x68747541 /* Auth */ +}; + +/* Processor Vendor and Models. */ + +enum processor_vendor +{ + VENDOR_INTEL = 1, + VENDOR_AMD, + VENDOR_OTHER, + VENDOR_MAX +}; + +enum processor_types +{ + INTEL_ATOM = 1, + INTEL_CORE2, + INTEL_COREI7, + AMDFAM10H, + AMDFAM15H, + CPU_TYPE_MAX +}; + +enum processor_subtypes +{ + INTEL_COREI7_NEHALEM = 1, + INTEL_COREI7_WESTMERE, + INTEL_COREI7_SANDYBRIDGE, + AMDFAM10H_BARCELONA, + AMDFAM10H_SHANGHAI, + AMDFAM10H_ISTANBUL, + AMDFAM15H_BDVER1, + AMDFAM15H_BDVER2, + CPU_SUBTYPE_MAX +}; + +/* ISA Features supported. */ + +enum processor_features +{ + FEATURE_CMOV = 0, + FEATURE_MMX, + FEATURE_POPCNT, + FEATURE_SSE, + FEATURE_SSE2, + FEATURE_SSE3, + FEATURE_SSSE3, + FEATURE_SSE4_1, + FEATURE_SSE4_2, + FEATURE_AVX, + FEATURE_AVX2 +}; + +struct __processor_model +{ + unsigned int __cpu_vendor; + unsigned int __cpu_type; + unsigned int __cpu_subtype; + unsigned int __cpu_features[1]; +} __cpu_model; + + +/* Get the specific type of AMD CPU. */ + +static void +get_amd_cpu (unsigned int family, unsigned int model) +{ + switch (family) + { + /* AMD Family 10h. */ + case 0x10: + switch (model) + { + case 0x2: + /* Barcelona. */ + __cpu_model.__cpu_type = AMDFAM10H; + __cpu_model.__cpu_subtype = AMDFAM10H_BARCELONA; + break; + case 0x4: + /* Shanghai. */ + __cpu_model.__cpu_type = AMDFAM10H; + __cpu_model.__cpu_subtype = AMDFAM10H_SHANGHAI; + break; + case 0x8: + /* Istanbul. */ + __cpu_model.__cpu_type = AMDFAM10H; + __cpu_model.__cpu_subtype = AMDFAM10H_ISTANBUL; + break; + default: + break; + } + break; + /* AMD Family 15h. */ + case 0x15: + __cpu_model.__cpu_type = AMDFAM15H; + /* Bulldozer version 1. */ + if ( model <= 0xf) + __cpu_model.__cpu_subtype = AMDFAM15H_BDVER1; + /* Bulldozer version 2. */ + if (model >= 0x10 && model <= 0x1f) + __cpu_model.__cpu_subtype = AMDFAM15H_BDVER2; + break; + default: + break; + } +} + +/* Get the specific type of Intel CPU. */ + +static void +get_intel_cpu (unsigned int family, unsigned int model, unsigned int brand_id) +{ + /* Parse family and model only if brand ID is 0. */ + if (brand_id == 0) + { + switch (family) + { + case 0x5: + /* Pentium. */ + break; + case 0x6: + switch (model) + { + case 0x1c: + case 0x26: + /* Atom. */ + __cpu_model.__cpu_type = INTEL_ATOM; + break; + case 0x1a: + case 0x1e: + case 0x1f: + case 0x2e: + /* Nehalem. */ + __cpu_model.__cpu_type = INTEL_COREI7; + __cpu_model.__cpu_subtype = INTEL_COREI7_NEHALEM; + break; + case 0x25: + case 0x2c: + case 0x2f: + /* Westmere. */ + __cpu_model.__cpu_type = INTEL_COREI7; + __cpu_model.__cpu_subtype = INTEL_COREI7_WESTMERE; + break; + case 0x2a: + /* Sandy Bridge. */ + __cpu_model.__cpu_type = INTEL_COREI7; + __cpu_model.__cpu_subtype = INTEL_COREI7_SANDYBRIDGE; + break; + case 0x17: + case 0x1d: + /* Penryn. */ + case 0x0f: + /* Merom. */ + __cpu_model.__cpu_type = INTEL_CORE2; + break; + default: + break; + } + break; + default: + /* We have no idea. */ + break; + } + } +} + +/* ECX and EDX are output of CPUID at level one. MAX_CPUID_LEVEL is + the max possible level of CPUID insn. */ +static void +get_available_features (unsigned int ecx, unsigned int edx, + int max_cpuid_level) +{ + unsigned int features = 0; + + if (edx & bit_CMOV) + features |= (1 << FEATURE_CMOV); + if (edx & bit_MMX) + features |= (1 << FEATURE_MMX); + if (edx & bit_SSE) + features |= (1 << FEATURE_SSE); + if (edx & bit_SSE2) + features |= (1 << FEATURE_SSE2); + if (ecx & bit_POPCNT) + features |= (1 << FEATURE_POPCNT); + if (ecx & bit_SSE3) + features |= (1 << FEATURE_SSE3); + if (ecx & bit_SSSE3) + features |= (1 << FEATURE_SSSE3); + if (ecx & bit_SSE4_1) + features |= (1 << FEATURE_SSE4_1); + if (ecx & bit_SSE4_2) + features |= (1 << FEATURE_SSE4_2); + if (ecx & bit_AVX) + features |= (1 << FEATURE_AVX); + + /* Get Advanced Features at level 7 (eax = 7, ecx = 0). */ + if (max_cpuid_level >= 7) + { + unsigned int eax, ebx, ecx, edx; + __cpuid_count (7, 0, eax, ebx, ecx, edx); + if (ebx & bit_AVX2) + features |= (1 << FEATURE_AVX2); + } + + __cpu_model.__cpu_features[0] = features; +} + +/* A noinline function calling __get_cpuid. Having many calls to + cpuid in one function in 32-bit mode causes GCC to complain: + "can't find a register in class CLOBBERED_REGS". This is + related to PR rtl-optimization 44174. */ + +static int __attribute__ ((noinline)) +__get_cpuid_output (unsigned int __level, + unsigned int *__eax, unsigned int *__ebx, + unsigned int *__ecx, unsigned int *__edx) +{ + return __get_cpuid (__level, __eax, __ebx, __ecx, __edx); +} + + +/* A constructor function that is sets __cpu_model and __cpu_features with + the right values. This needs to run only once. This constructor is + given the highest priority and it should run before constructors without + the priority set. However, it still runs after ifunc initializers and + needs to be called explicitly there. */ + +int __attribute__ ((constructor CONSTRUCTOR_PRIORITY)) +__cpu_indicator_init (void) +{ + unsigned int eax, ebx, ecx, edx; + + int max_level = 5; + unsigned int vendor; + unsigned int model, family, brand_id; + unsigned int extended_model, extended_family; + + /* This function needs to run just once. */ + if (__cpu_model.__cpu_vendor) + return 0; + + /* Assume cpuid insn present. Run in level 0 to get vendor id. */ + if (!__get_cpuid_output (0, &eax, &ebx, &ecx, &edx)) + { + __cpu_model.__cpu_vendor = VENDOR_OTHER; + return -1; + } + + vendor = ebx; + max_level = eax; + + if (max_level < 1) + { + __cpu_model.__cpu_vendor = VENDOR_OTHER; + return -1; + } + + if (!__get_cpuid_output (1, &eax, &ebx, &ecx, &edx)) + { + __cpu_model.__cpu_vendor = VENDOR_OTHER; + return -1; + } + + model = (eax >> 4) & 0x0f; + family = (eax >> 8) & 0x0f; + brand_id = ebx & 0xff; + extended_model = (eax >> 12) & 0xf0; + extended_family = (eax >> 20) & 0xff; + + if (vendor == SIG_INTEL) + { + /* Adjust model and family for Intel CPUS. */ + if (family == 0x0f) + { + family += extended_family; + model += extended_model; + } + else if (family == 0x06) + model += extended_model; + + /* Get CPU type. */ + get_intel_cpu (family, model, brand_id); + /* Find available features. */ + get_available_features (ecx, edx, max_level); + __cpu_model.__cpu_vendor = VENDOR_INTEL; + } + else if (vendor == SIG_AMD) + { + /* Adjust model and family for AMD CPUS. */ + if (family == 0x0f) + { + family += extended_family; + model += (extended_model << 4); + } + + /* Get CPU type. */ + get_amd_cpu (family, model); + /* Find available features. */ + get_available_features (ecx, edx, max_level); + __cpu_model.__cpu_vendor = VENDOR_AMD; + } + else + __cpu_model.__cpu_vendor = VENDOR_OTHER; + + gcc_assert (__cpu_model.__cpu_vendor < VENDOR_MAX); + gcc_assert (__cpu_model.__cpu_type < CPU_TYPE_MAX); + gcc_assert (__cpu_model.__cpu_subtype < CPU_SUBTYPE_MAX); + + return 0; +} |