aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/i386/driver-i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i386/driver-i386.c')
-rw-r--r--gcc/config/i386/driver-i386.c149
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