===================================================================
@@ -121,7 +121,7 @@ struct aarch64_option_extension
/* ISA extensions in AArch64. */
static const struct aarch64_option_extension all_extensions[] =
{
-#define AARCH64_OPT_EXTENSION(NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, Z) \
+#define AARCH64_OPT_EXTENSION(NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, Z, YY) \
{NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF},
#include "config/aarch64/aarch64-option-extensions.def"
{NULL, 0, 0, 0}
===================================================================
@@ -21,7 +21,7 @@
Before using #include to read this file, define a macro:
- AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, FEATURE_STRING)
+ AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, FEATURE_STRING, HWCAP)
EXT_NAME is the name of the extension, represented as a string constant.
FLAGS_CANONICAL is the canonical internal name for this flag.
@@ -36,28 +36,29 @@
the extension (for example, the 'crypto' extension depends on four
entries: aes, pmull, sha1, sha2 being present). In that case this field
should contain a space (" ") separated list of the strings in 'Features'
- that are required. Their order is not important. */
+ that are required. Their order is not important.
+ HWCAP is the required hwcap mask for this feature. */
/* Enabling "fp" just enables "fp".
Disabling "fp" also disables "simd", "crypto" and "fp16". */
-AARCH64_OPT_EXTENSION("fp", AARCH64_FL_FP, 0, AARCH64_FL_SIMD | AARCH64_FL_CRYPTO | AARCH64_FL_F16, "fp")
+AARCH64_OPT_EXTENSION("fp", AARCH64_FL_FP, 0, AARCH64_FL_SIMD | AARCH64_FL_CRYPTO | AARCH64_FL_F16, "fp", HWCAP_FP)
/* Enabling "simd" also enables "fp".
Disabling "simd" also disables "crypto". */
-AARCH64_OPT_EXTENSION("simd", AARCH64_FL_SIMD, AARCH64_FL_FP, AARCH64_FL_CRYPTO, "asimd")
+AARCH64_OPT_EXTENSION("simd", AARCH64_FL_SIMD, AARCH64_FL_FP, AARCH64_FL_CRYPTO, "asimd", HWCAP_ASIMD)
/* Enabling "crypto" also enables "fp", "simd".
Disabling "crypto" just disables "crypto". */
-AARCH64_OPT_EXTENSION("crypto", AARCH64_FL_CRYPTO, AARCH64_FL_FP | AARCH64_FL_SIMD, 0, "aes pmull sha1 sha2")
+AARCH64_OPT_EXTENSION("crypto", AARCH64_FL_CRYPTO, AARCH64_FL_FP | AARCH64_FL_SIMD, 0, "aes pmull sha1 sha2", HWCAP_AES | HWCAP_PMULL | HWCAP_SHA1 | HWCAP_SHA2)
/* Enabling or disabling "crc" only changes "crc". */
-AARCH64_OPT_EXTENSION("crc", AARCH64_FL_CRC, 0, 0, "crc32")
+AARCH64_OPT_EXTENSION("crc", AARCH64_FL_CRC, 0, 0, "crc32", HWCAP_CRC32)
/* Enabling or disabling "lse" only changes "lse". */
-AARCH64_OPT_EXTENSION("lse", AARCH64_FL_LSE, 0, 0, "atomics")
+AARCH64_OPT_EXTENSION("lse", AARCH64_FL_LSE, 0, 0, "atomics", HWCAP_ATOMICS)
/* Enabling "fp16" also enables "fp".
Disabling "fp16" just disables "fp16". */
-AARCH64_OPT_EXTENSION("fp16", AARCH64_FL_F16, AARCH64_FL_FP, 0, "fp16")
+AARCH64_OPT_EXTENSION("fp16", AARCH64_FL_F16, AARCH64_FL_FP, 0, "fp16", HWCAP_FPHP | HWCAP_ASIMDHP)
#undef AARCH64_OPT_EXTENSION
===================================================================
@@ -22,20 +22,40 @@
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include <sys/auxv.h>
+#include <asm/hwcap.h>
/* Defined in common/config/aarch64/aarch64-common.c. */
std::string aarch64_get_extension_string_for_isa_flags (unsigned long,
unsigned long);
+/* Not always defined hwcaps. */
+#ifndef HWCAP_CRC32
+#define HWCAP_CRC32 (1 << 7)
+#endif
+
+#ifndef HWCAP_ATOMICS
+#define HWCAP_ATOMICS (1 << 8)
+#endif
+
+#ifndef HWCAP_FPHP
+#define HWCAP_FPHP (1 << 9)
+#endif
+
+#ifndef HWCAP_ASIMDHP
+#define HWCAP_ASIMDHP (1 << 10)
+#endif
+
struct aarch64_arch_extension
{
const char *ext;
unsigned int flag;
const char *feat_string;
+ unsigned long hwcap_mask;
};
-#define AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, FEATURE_STRING) \
- { EXT_NAME, FLAG_CANONICAL, FEATURE_STRING },
+#define AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, FEATURE_STRING, HWCAP_MASK) \
+ { EXT_NAME, FLAG_CANONICAL, FEATURE_STRING, HWCAP_MASK },
static struct aarch64_arch_extension aarch64_extensions[] =
{
#include "aarch64-option-extensions.def"
@@ -51,8 +71,10 @@ struct aarch64_core_data
const unsigned long flags;
};
-#define AARCH64_BIG_LITTLE(BIG, LITTLE) \
- (((BIG)&0xFFFu) << 12 | ((LITTLE) & 0xFFFu))
+#define AARCH64_BIG_LITTLE(BIG, LITTLE) \
+ (((BIG) > (LITTLE)) \
+ ? (((BIG)&0xFFFu) << 12 | ((LITTLE) & 0xFFFu)) \
+ : (((LITTLE)&0xFFFu) << 12 | ((BIG) & 0xFFFu)))
#define INVALID_IMP ((unsigned char) -1)
#define INVALID_CORE ((unsigned)-1)
@@ -107,8 +129,7 @@ get_arch_from_id (const char* id)
static bool
valid_bL_core_p (unsigned int *core, unsigned int bL_core)
{
- return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core
- || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core;
+ return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core;
}
/* Returns the hex integer that is after ':' for the FIELD.
@@ -141,6 +162,186 @@ contains_core_p (unsigned *arr, unsigned
return false;
}
+/* Returns the implementator from MIDR. */
+
+static inline unsigned
+implementor_from_midr (unsigned midr)
+{
+ return midr >> 24;
+}
+
+
+/* Returns the part number from MIDR. */
+
+static inline unsigned
+part_no_from_midr (unsigned midr)
+{
+ return (midr & 0xffff) >> 4;
+}
+
+#define sysfsformat "/sys/devices/system/cpu/cpu%d/regs/identification/midr_el1"
+
+
+/* Parses the sysfs if it exits. This has higher prority than parsing
+ /proc/cpuinfo.
+ TODO: This should also parse out the cache info too. */
+
+static const char *
+host_detect_local_cpu_sys (FILE *f, bool cpu, bool tune, bool arch)
+{
+ static const int num_exts = ARRAY_SIZE (aarch64_extensions);
+ const char *res = NULL;
+ char present[128];
+ unsigned midrs[2] = { INVALID_CORE, INVALID_CORE };
+ int n_midr = 0;
+ unsigned long extension_flags = 0;
+ unsigned long default_flags = 0;
+ const char *ext_string = "";
+
+ /* Read present, it is formated as comma seperated min-max. */
+ bool r = fgets (present, sizeof (present), f) == NULL;
+ fclose (f);
+ if (r)
+ return NULL;
+ /* Remove the newline. */
+ if (present[strlen(present)-1]=='\n')
+ present[strlen(present)-1] = 0;
+
+ char *nextfield = present;
+ do
+ {
+ int min;
+ int max;
+ char *current = nextfield;
+ char *dash;
+ min = strtol (current, &dash, 0);
+ if (dash[0] == 0)
+ {
+ max = min;
+ nextfield = NULL;
+ }
+ if (dash[0] == ',')
+ {
+ max = min;
+ nextfield = dash + 1;
+ }
+ /* Parse error */
+ else if (dash[0] != '-')
+ return NULL;
+ else
+ {
+ max = strtol (dash+1, &dash, 0);
+ if (dash[0] == 0)
+ nextfield = NULL;
+ else if (dash[0] != ',')
+ return NULL;
+ else
+ nextfield = dash + 1;
+ }
+
+ for (int cpu = min; cpu <= max; cpu++)
+ {
+ char buf[128];
+ char *end;
+ char filename[sizeof(sysfsformat)+5];
+ sprintf(filename, sysfsformat, cpu);
+ f = fopen (filename, "r");
+ if (!f)
+ return NULL;
+ r = fgets (buf, sizeof (buf), f) == NULL;
+ fclose (f);
+ if (r)
+ return NULL;
+ unsigned midr = strtoul(buf, &end, 0);
+ if (end[0] != 0 && end[0] != '\n')
+ return NULL;
+ if (!contains_core_p (midrs, midr))
+ {
+ if (n_midr == 2)
+ return NULL;
+ midrs[n_midr++] = midr;
+ }
+ }
+ } while (nextfield);
+
+ /* No processor, then can't find anything. */
+ if (n_midr == 0)
+ return NULL;
+
+ /* Find the features via the HWCAP. */
+ unsigned long hwcap = getauxval (AT_HWCAP);
+ for (int i = 0; i < num_exts; i++)
+ if (hwcap & aarch64_extensions[i].hwcap_mask)
+ extension_flags |= aarch64_extensions[i].flag;
+ else
+ extension_flags &= ~aarch64_extensions[i].flag;
+
+ /* Found one type of core. */
+ unsigned imp;
+ unsigned part_no;
+ if (n_midr == 1)
+ {
+ imp = implementor_from_midr (midrs[0]);
+ part_no = (midrs[0] & 0xffff) >> 4;
+ }
+ else
+ {
+ unsigned imp0 = implementor_from_midr (midrs[0]);
+ unsigned part_no0 = (midrs[0] & 0xffff) >> 4;
+ unsigned imp1 = implementor_from_midr (midrs[1]);
+ unsigned part_no1 = (midrs[1] & 0xffff) >> 4;
+ /* FIXME: Handle the case where there are two implementors. */
+ if (imp1 != imp0)
+ return NULL;
+ imp = imp0;
+ if (part_no0 != part_no1)
+ part_no = AARCH64_BIG_LITTLE (part_no0, part_no1);
+ else
+ part_no = part_no0;
+ }
+
+ int core_idx = -1;
+ for (int i = 0; aarch64_cpu_data[i].name != NULL; i++)
+ if (part_no == aarch64_cpu_data[i].part_no
+ && aarch64_cpu_data[i].implementer_id == imp)
+ {
+ core_idx = i;
+ break;
+ }
+ if (core_idx == -1)
+ return NULL;
+
+ if (arch)
+ {
+ struct aarch64_arch_driver_info* arch_info
+ = get_arch_from_id (aarch64_cpu_data [core_idx].arch);
+ /* We got some arch indentifier that's not in aarch64-arches.def? */
+ if (!arch_info)
+ return NULL;
+ res = concat ("-march=", arch_info->name, NULL);
+ default_flags = arch_info->flags;
+ }
+ else
+ {
+ res = concat ("-m", cpu ? "cpu" : "tune", "=",
+ aarch64_cpu_data[core_idx].name, NULL);
+ default_flags = aarch64_cpu_data[core_idx].flags;
+ }
+
+ //FIXME: Detect the caches here via /sys/devices/system/cpu/cpu%d/cache%d.
+
+ if (tune)
+ return res;
+
+ ext_string
+ = aarch64_get_extension_string_for_isa_flags (extension_flags,
+ default_flags).c_str ();
+
+ res = concat (res, ext_string, NULL);
+ return res;
+}
+
+
/* 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", "cpu" or "tune" as argument depending on if
@@ -193,6 +394,16 @@ host_detect_local_cpu (int argc, const c
if (!arch && !tune && !cpu)
goto not_found;
+
+ f = fopen ("/sys/devices/system/cpu/present", "r");
+
+ if (f)
+ {
+ res = host_detect_local_cpu_sys (f, cpu, tune, arch);
+ if (res)
+ return res;
+ }
+
f = fopen ("/proc/cpuinfo", "r");
if (f == NULL)