diff options
Diffstat (limited to 'gcc/config/i386/driver-i386.c')
-rw-r--r-- | gcc/config/i386/driver-i386.c | 149 |
1 files changed, 148 insertions, 1 deletions
diff --git a/gcc/config/i386/driver-i386.c b/gcc/config/i386/driver-i386.c index df43512f775..a4155373f1a 100644 --- a/gcc/config/i386/driver-i386.c +++ b/gcc/config/i386/driver-i386.c @@ -43,10 +43,136 @@ const char *host_detect_local_cpu (int argc, const char **argv); #define bit_SSE4a (1 << 6) #define bit_CMPXCHG16B (1 << 13) +#define bit_LAHF_LM (1 << 0) #define bit_3DNOW (1 << 31) #define bit_3DNOWP (1 << 30) #define bit_LM (1 << 29) +/* Returns parameters that describe L1_ASSOC associative cache of size + L1_SIZEKB with lines of size L1_LINE. */ + +static char * +describe_cache (unsigned l1_sizekb, unsigned l1_line, + unsigned l1_assoc ATTRIBUTE_UNUSED) +{ + char size[1000], line[1000]; + unsigned size_in_lines; + + /* At the moment, gcc middle-end does not use the information about the + associativity of the cache. */ + + size_in_lines = (l1_sizekb * 1024) / l1_line; + + sprintf (size, "--param l1-cache-size=%u", size_in_lines); + sprintf (line, "--param l1-cache-line-size=%u", l1_line); + + return concat (size, " ", line, " ", NULL); +} + +/* Returns the description of caches for an AMD processor. */ + +static char * +detect_caches_amd (unsigned max_ext_level) +{ + unsigned eax, ebx, ecx, edx; + unsigned l1_sizekb, l1_line, l1_assoc; + + if (max_ext_level < 0x80000005) + return NULL; + + cpuid (0x80000005, eax, ebx, ecx, edx); + + l1_line = ecx & 0xff; + l1_sizekb = (ecx >> 24) & 0xff; + l1_assoc = (ecx >> 16) & 0xff; + + return describe_cache (l1_sizekb, l1_line, l1_assoc); +} + +/* Stores the size of the L1 cache and cache line, and the associativity + of the cache according to REG to L1_SIZEKB, L1_LINE and L1_ASSOC. */ + +static void +decode_caches_intel (unsigned reg, unsigned *l1_sizekb, unsigned *l1_line, + unsigned *l1_assoc) +{ + unsigned i, val; + + if (((reg >> 31) & 1) != 0) + return; + + for (i = 0; i < 4; i++) + { + val = reg & 0xff; + reg >>= 8; + + switch (val) + { + case 0xa: + *l1_sizekb = 8; + *l1_line = 32; + *l1_assoc = 2; + break; + case 0xc: + *l1_sizekb = 16; + *l1_line = 32; + *l1_assoc = 4; + break; + case 0x2c: + *l1_sizekb = 32; + *l1_line = 64; + *l1_assoc = 8; + break; + case 0x60: + *l1_sizekb = 16; + *l1_line = 64; + *l1_assoc = 8; + break; + case 0x66: + *l1_sizekb = 8; + *l1_line = 64; + *l1_assoc = 4; + break; + case 0x67: + *l1_sizekb = 16; + *l1_line = 64; + *l1_assoc = 4; + break; + case 0x68: + *l1_sizekb = 32; + *l1_line = 64; + *l1_assoc = 4; + break; + + default: + break; + } + } +} + +/* Returns the description of caches for an intel processor. */ + +static char * +detect_caches_intel (unsigned max_level) +{ + unsigned eax, ebx, ecx, edx; + unsigned l1_sizekb = 0, l1_line = 0, assoc = 0; + + if (max_level < 2) + return NULL; + + cpuid (2, eax, ebx, ecx, edx); + + decode_caches_intel (eax, &l1_sizekb, &l1_line, &assoc); + decode_caches_intel (ebx, &l1_sizekb, &l1_line, &assoc); + decode_caches_intel (ecx, &l1_sizekb, &l1_line, &assoc); + decode_caches_intel (edx, &l1_sizekb, &l1_line, &assoc); + if (!l1_sizekb) + return (char *) ""; + + return describe_cache (l1_sizekb, l1_line, assoc); +} + /* 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 @@ -62,6 +188,8 @@ const char *host_detect_local_cpu (int argc, const char **argv); const char *host_detect_local_cpu (int argc, const char **argv) { const char *cpu = NULL; + const char *cache = ""; + const char *options = ""; enum processor_type processor = PROCESSOR_I386; unsigned int eax, ebx, ecx, edx; unsigned int max_level; @@ -69,6 +197,7 @@ const char *host_detect_local_cpu (int argc, const char **argv) 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_cmpxchg16b = 0, has_lahf_lm = 0; unsigned char has_longmode = 0, has_cmpxchg8b = 0, has_sse4a = 0; unsigned char is_amd = 0; unsigned int family = 0; @@ -110,6 +239,7 @@ const char *host_detect_local_cpu (int argc, const char **argv) has_sse2 = !!(edx & bit_SSE2); has_sse3 = !!(ecx & bit_SSE3); has_ssse3 = !!(ecx & bit_SSSE3); + has_cmpxchg16b = !!(ecx & bit_CMPXCHG16B); /* We don't care for extended family. */ family = (eax >> 8) & ~(1 << 4); @@ -118,6 +248,7 @@ const char *host_detect_local_cpu (int argc, const char **argv) if (ext_level >= 0x80000000) { cpuid (0x80000001, eax, ebx, ecx, edx); + has_lahf_lm = !!(ecx & bit_LAHF_LM); has_3dnow = !!(edx & bit_3DNOW); has_3dnowp = !!(edx & bit_3DNOWP); has_longmode = !!(edx & bit_LM); @@ -126,6 +257,14 @@ const char *host_detect_local_cpu (int argc, const char **argv) is_amd = vendor == *(unsigned int*)"Auth"; + if (!arch) + { + if (is_amd) + cache = detect_caches_amd (ext_level); + else if (vendor == *(unsigned int*)"Genu") + cache = detect_caches_intel (max_level); + } + if (is_amd) { if (has_mmx) @@ -282,8 +421,16 @@ const char *host_detect_local_cpu (int argc, const char **argv) break; } + if (arch) + { + if (has_cmpxchg16b) + options = concat (options, "-mcx16 ", NULL); + if (has_lahf_lm) + options = concat (options, "-msahf ", NULL); + } + done: - return concat ("-m", argv[0], "=", cpu, NULL); + return concat (cache, "-m", argv[0], "=", cpu, " ", options, NULL); } #else /* If we aren't compiling with GCC we just provide a minimal |