1 /* Native CPU detection for aarch64.
2 Copyright (C) 2015-2020 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
19
20 #define IN_TARGET_CODE 1
21
22 #include "config.h"
23 #define INCLUDE_STRING
24 #define INCLUDE_SET
25 #include "system.h"
26 #include "coretypes.h"
27 #include "tm.h"
28 #include "diagnostic-core.h"
29
30 /* Defined in common/config/aarch64/aarch64-common.c. */
31 std::string aarch64_get_extension_string_for_isa_flags (uint64_t, uint64_t);
32
33 struct aarch64_arch_extension
34 {
35 const char *ext;
36 uint64_t flag;
37 const char *feat_string;
38 };
39
40 #define AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, \
41 SYNTHETIC, FEATURE_STRING) \
42 { EXT_NAME, FLAG_CANONICAL, FEATURE_STRING },
43 static struct aarch64_arch_extension aarch64_extensions[] =
44 {
45 #include "aarch64-option-extensions.def"
46 };
47
48
49 struct aarch64_core_data
50 {
51 const char* name;
52 const char* arch;
53 unsigned char implementer_id; /* Exactly 8 bits */
54 unsigned int part_no; /* 12 bits + 12 bits */
55 unsigned variant;
56 const uint64_t flags;
57 };
58
59 #define AARCH64_BIG_LITTLE(BIG, LITTLE) \
60 (((BIG)&0xFFFu) << 12 | ((LITTLE) & 0xFFFu))
61 #define INVALID_IMP ((unsigned char) -1)
62 #define INVALID_CORE ((unsigned)-1)
63 #define ALL_VARIANTS ((unsigned)-1)
64
65 #define AARCH64_CORE(CORE_NAME, CORE_IDENT, SCHED, ARCH, FLAGS, COSTS, IMP, PART, VARIANT) \
66 { CORE_NAME, #ARCH, IMP, PART, VARIANT, FLAGS },
67
68 static struct aarch64_core_data aarch64_cpu_data[] =
69 {
70 #include "aarch64-cores.def"
71 { NULL, NULL, INVALID_IMP, INVALID_CORE, ALL_VARIANTS, 0 }
72 };
73
74
75 struct aarch64_arch_driver_info
76 {
77 const char* id;
78 const char* name;
79 const uint64_t flags;
80 };
81
82 #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \
83 { #ARCH_IDENT, NAME, FLAGS },
84
85 static struct aarch64_arch_driver_info aarch64_arches[] =
86 {
87 #include "aarch64-arches.def"
88 {NULL, NULL, 0}
89 };
90
91
92 /* Return an aarch64_arch_driver_info for the architecture described
93 by ID, or NULL if ID describes something we don't know about. */
94
95 static struct aarch64_arch_driver_info*
get_arch_from_id(const char * id)96 get_arch_from_id (const char* id)
97 {
98 unsigned int i = 0;
99
100 for (i = 0; aarch64_arches[i].id != NULL; i++)
101 {
102 if (strcmp (id, aarch64_arches[i].id) == 0)
103 return &aarch64_arches[i];
104 }
105
106 return NULL;
107 }
108
109 /* Check wether the CORE array is the same as the big.LITTLE BL_CORE.
110 For an example CORE={0xd08, 0xd03} and
111 BL_CORE=AARCH64_BIG_LITTLE (0xd08, 0xd03) will return true. */
112
113 static bool
valid_bL_core_p(unsigned int * core,unsigned int bL_core)114 valid_bL_core_p (unsigned int *core, unsigned int bL_core)
115 {
116 return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core
117 || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core;
118 }
119
120 /* Returns the hex integer that is after ':' for the FIELD.
121 Returns -1 is returned if there was problem parsing the integer. */
122 static unsigned
parse_field(const std::string & field)123 parse_field (const std::string &field)
124 {
125 const char *rest = strchr (field.c_str (), ':');
126
127 /* The line must be in the format of <name>:<value>, if it's not
128 then we have a weird format. */
129 if (rest == NULL)
130 return -1;
131
132 char *after;
133 unsigned fint = strtol (rest + 1, &after, 16);
134 if (after == rest + 1)
135 return -1;
136 return fint;
137 }
138
139 /* Returns the index of the ':' inside the FIELD which must be found
140 after the value of KEY. Returns string::npos if line does not contain
141 a field. */
142
143 static size_t
find_field(const std::string & field,const std::string & key)144 find_field (const std::string &field, const std::string &key)
145 {
146 size_t key_pos, sep_pos;
147 key_pos = field.find (key);
148 if (key_pos == std::string::npos)
149 return std::string::npos;
150
151 sep_pos = field.find (":", key_pos + 1);
152 if (sep_pos == std::string::npos)
153 return std::string::npos;
154
155 return sep_pos;
156 }
157
158 /* Splits and returns a string based on whitespace and return it as
159 part of a set. Empty strings are ignored. */
160
161 static void
split_words(const std::string & val,std::set<std::string> & result)162 split_words (const std::string &val, std::set<std::string> &result)
163 {
164 size_t cur, prev = 0;
165 std::string word;
166 while ((cur = val.find_first_of (" \n", prev)) != std::string::npos)
167 {
168 word = val.substr (prev, cur - prev);
169 /* Skip adding empty words. */
170 if (!word.empty ())
171 result.insert (word);
172 prev = cur + 1;
173 }
174
175 if (prev != cur)
176 result.insert (val.substr (prev));
177 }
178
179 /* Read an entire line from F until '\n' or EOF. */
180
181 static std::string
readline(FILE * f)182 readline (FILE *f)
183 {
184 char *buf = NULL;
185 int size = 0;
186 int last = 0;
187 const int buf_size = 128;
188
189 if (feof (f))
190 return std::string ();
191
192 do
193 {
194 size += buf_size;
195 buf = (char*) xrealloc (buf, size);
196 gcc_assert (buf);
197 /* If fgets fails it returns NULL, but if it reaches EOF
198 with 0 characters read it also returns EOF. However
199 the condition on the loop would have broken out of the
200 loop in that case, and if we are in the first iteration
201 then the empty string is the correct thing to return. */
202 if (!fgets (buf + last, buf_size, f))
203 return std::string ();
204 /* If we're not at the end of the line then override the
205 \0 added by fgets. */
206 last = strnlen (buf, size);
207 }
208 while (!feof (f) && last > 0 && buf[last - 1] != '\n');
209
210 std::string result (buf);
211 free (buf);
212 return result;
213 }
214
215 /* Return true iff ARR contains CORE, in either of the two elements. */
216
217 static bool
contains_core_p(unsigned * arr,unsigned core)218 contains_core_p (unsigned *arr, unsigned core)
219 {
220 if (arr[0] != INVALID_CORE)
221 {
222 if (arr[0] == core)
223 return true;
224
225 if (arr[1] != INVALID_CORE)
226 return arr[1] == core;
227 }
228
229 return false;
230 }
231
232 /* This will be called by the spec parser in gcc.c when it sees
233 a %:local_cpu_detect(args) construct. Currently it will be called
234 with either "arch", "cpu" or "tune" as argument depending on if
235 -march=native, -mcpu=native or -mtune=native is to be substituted.
236
237 It returns a string containing new command line parameters to be
238 put at the place of the above two options, depending on what CPU
239 this is executed. E.g. "-march=armv8-a" on a Cortex-A57 for
240 -march=native. If the routine can't detect a known processor,
241 the -march or -mtune option is discarded.
242
243 For -mtune and -mcpu arguments it attempts to detect the CPU or
244 a big.LITTLE system.
245 ARGC and ARGV are set depending on the actual arguments given
246 in the spec. */
247
248 #ifdef __NetBSD__
249 /* The NetBSD/arm64 platform may not export linux-style /proc/cpuinfo,
250 but the data is available via a sysctl(3) interface. */
251 #include <sys/param.h>
252 #include <sys/sysctl.h>
253 #include <aarch64/armreg.h>
254 #endif
255
256 const char *
host_detect_local_cpu(int argc,const char ** argv)257 host_detect_local_cpu (int argc, const char **argv)
258 {
259 const char *res = NULL;
260 static const int num_exts = ARRAY_SIZE (aarch64_extensions);
261 FILE *f = NULL;
262 bool arch = false;
263 bool tune = false;
264 bool cpu = false;
265 unsigned int i = 0;
266 unsigned char imp = INVALID_IMP;
267 unsigned int cores[2] = { INVALID_CORE, INVALID_CORE };
268 unsigned int n_cores = 0;
269 unsigned int variants[2] = { ALL_VARIANTS, ALL_VARIANTS };
270 unsigned int n_variants = 0;
271 bool processed_exts = false;
272 uint64_t extension_flags = 0;
273 uint64_t default_flags = 0;
274 std::string buf;
275 size_t sep_pos = -1;
276 char *fcpu_info;
277
278 gcc_assert (argc);
279
280 if (!argv[0])
281 goto not_found;
282
283 /* Are we processing -march, mtune or mcpu? */
284 arch = strcmp (argv[0], "arch") == 0;
285 if (!arch)
286 tune = strcmp (argv[0], "tune") == 0;
287
288 if (!arch && !tune)
289 cpu = strcmp (argv[0], "cpu") == 0;
290
291 if (!arch && !tune && !cpu)
292 goto not_found;
293
294 #ifndef __NetBSD__
295 fcpu_info = getenv ("GCC_CPUINFO");
296 if (fcpu_info)
297 f = fopen (fcpu_info, "r");
298 else
299 f = fopen ("/proc/cpuinfo", "r");
300
301 if (f == NULL)
302 goto not_found;
303
304 /* Look through /proc/cpuinfo to determine the implementer
305 and then the part number that identifies a particular core. */
306 while (!(buf = readline (f)).empty ())
307 {
308 if (find_field (buf, "implementer") != std::string::npos)
309 {
310 unsigned cimp = parse_field (buf);
311 if (cimp == INVALID_IMP)
312 goto not_found;
313
314 if (imp == INVALID_IMP)
315 imp = cimp;
316 /* FIXME: BIG.little implementers are always equal. */
317 else if (imp != cimp)
318 goto not_found;
319 }
320 else if (find_field (buf, "variant") != std::string::npos)
321 {
322 unsigned cvariant = parse_field (buf);
323 if (!contains_core_p (variants, cvariant))
324 {
325 if (n_variants == 2)
326 goto not_found;
327
328 variants[n_variants++] = cvariant;
329 }
330 continue;
331 }
332 else if (find_field (buf, "part") != std::string::npos)
333 {
334 unsigned ccore = parse_field (buf);
335 if (!contains_core_p (cores, ccore))
336 {
337 if (n_cores == 2)
338 goto not_found;
339
340 cores[n_cores++] = ccore;
341 }
342 continue;
343 }
344 else if (!tune && !processed_exts
345 && (sep_pos = find_field (buf, "Features")) != std::string::npos)
346 {
347 /* First create the list of features in the buffer. */
348 std::set<std::string> features;
349 /* Drop everything till the :. */
350 buf = buf.substr (sep_pos + 1);
351 split_words (buf, features);
352
353 for (i = 0; i < num_exts; i++)
354 {
355 const std::string val (aarch64_extensions[i].feat_string);
356
357 /* If the feature contains no HWCAPS string then ignore it for the
358 auto detection. */
359 if (val.empty ())
360 continue;
361
362 bool enabled = true;
363
364 /* This may be a multi-token feature string. We need
365 to match all parts, which could be in any order. */
366 std::set<std::string> tokens;
367 split_words (val, tokens);
368 std::set<std::string>::iterator it;
369
370 /* Iterate till the first feature isn't found or all of them
371 are found. */
372 for (it = tokens.begin (); enabled && it != tokens.end (); ++it)
373 enabled = enabled && features.count (*it);
374
375 if (enabled)
376 extension_flags |= aarch64_extensions[i].flag;
377 else
378 extension_flags &= ~(aarch64_extensions[i].flag);
379 }
380
381 processed_exts = true;
382 }
383 }
384
385 fclose (f);
386 f = NULL;
387 #else
388 unsigned int curcpu;
389 size_t len;
390 char impl_buf[8];
391 int mib[2], ncpu;
392
393 mib[0] = CTL_HW;
394 mib[1] = HW_NCPU;
395 len = sizeof(ncpu);
396 if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
397 goto not_found;
398
399 for (curcpu = 0; curcpu < ncpu; curcpu++)
400 {
401 char path[128];
402 struct aarch64_sysctl_cpu_id id;
403
404 len = sizeof id;
405 snprintf(path, sizeof path, "machdep.cpu%d.cpu_id", curcpu);
406 if (sysctlbyname(path, &id, &len, NULL, 0) != 0)
407 goto not_found;
408
409 unsigned cimp = __SHIFTOUT(id.ac_midr, MIDR_EL1_IMPL);
410 if (cimp == INVALID_IMP)
411 goto not_found;
412
413 if (imp == INVALID_IMP)
414 imp = cimp;
415 /* FIXME: BIG.little implementers are always equal. */
416 else if (imp != cimp)
417 goto not_found;
418
419 unsigned cvariant = __SHIFTOUT(id.ac_midr, MIDR_EL1_VARIANT);
420 if (!contains_core_p (variants, cvariant))
421 {
422 if (n_variants == 2)
423 goto not_found;
424
425 variants[n_variants++] = cvariant;
426 }
427
428 unsigned ccore = __SHIFTOUT(id.ac_midr, MIDR_EL1_PARTNUM);
429 if (!contains_core_p (cores, ccore))
430 {
431 if (n_cores == 2)
432 goto not_found;
433
434 cores[n_cores++] = ccore;
435 }
436
437 if (!tune && !processed_exts)
438 {
439 std::string exts;
440
441 /* These are all the extensions from aarch64-option-extensions.def. */
442 if (__SHIFTOUT(id.ac_aa64pfr0, ID_AA64PFR0_EL1_FP) == ID_AA64PFR0_EL1_FP_IMPL)
443 exts += "fp ";
444 if (__SHIFTOUT(id.ac_aa64pfr0, ID_AA64PFR0_EL1_ADVSIMD) == ID_AA64PFR0_EL1_ADV_SIMD_IMPL)
445 exts += "asimd ";
446 #ifdef ID_AA64ISAR0_EL1_RDM
447 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_RDM) == ID_AA64ISAR0_EL1_RDM_SQRDML)
448 exts += "asimdrdm ";
449 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_DP) == ID_AA64ISAR0_EL1_DP_UDOT)
450 exts += "asimddp ";
451 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_FHM) == ID_AA64ISAR0_EL1_FHM_FMLAL)
452 exts += "asimdfml ";
453 #endif
454 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_AES) == ID_AA64ISAR0_EL1_AES_AES)
455 exts += "aes ";
456 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_AES) == ID_AA64ISAR0_EL1_AES_PMUL)
457 exts += "aes pmull ";
458 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_CRC32) == ID_AA64ISAR0_EL1_CRC32_CRC32X)
459 exts += "crc32 ";
460 #ifdef ID_AA64ISAR0_EL1_ATOMIC
461 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_ATOMIC) == ID_AA64ISAR0_EL1_ATOMIC_SWP)
462 exts += "atomics ";
463 #endif
464 if ((__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SHA1) & ID_AA64ISAR0_EL1_SHA1_SHA1CPMHSU) != 0)
465 exts += "sha1 ";
466 if ((__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SHA2) & ID_AA64ISAR0_EL1_SHA2_SHA256HSU) != 0)
467 exts += "sha2 ";
468 #ifdef ID_AA64ISAR0_EL1_SHA2_SHA512HSU
469 if ((__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SHA2) & ID_AA64ISAR0_EL1_SHA2_SHA512HSU) != 0)
470 exts += "sha512 ";
471 if ((__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SHA3) & ID_AA64ISAR0_EL1_SHA3_EOR3) != 0)
472 exts += "sha3 ";
473 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SM3) == ID_AA64ISAR0_EL1_SM3_SM3)
474 exts += "sm3 ";
475 if (__SHIFTOUT(id.ac_aa64isar0, ID_AA64ISAR0_EL1_SM4) == ID_AA64ISAR0_EL1_SM4_SM4)
476 exts += "sm4 ";
477 if (__SHIFTOUT(id.ac_aa64pfr0, ID_AA64PFR0_EL1_SVE) == ID_AA64PFR0_EL1_SVE_IMPL)
478 exts += "sve ";
479 if (__SHIFTOUT(id.ac_aa64isar1, ID_AA64ISAR1_EL1_LRCPC) == ID_AA64ISAR1_EL1_LRCPC_PR)
480 exts += "lrcpc ";
481 #endif
482
483 for (i = 0; i < num_exts; i++)
484 {
485 const char *p = aarch64_extensions[i].feat_string;
486
487 /* If the feature contains no HWCAPS string then ignore it for the
488 auto detection. */
489 if (*p == '\0')
490 continue;
491
492 bool enabled = true;
493
494 /* This may be a multi-token feature string. We need
495 to match all parts, which could be in any order. */
496 size_t len = strlen (exts.c_str());
497 do
498 {
499 const char *end = strchr (p, ' ');
500 if (end == NULL)
501 end = strchr (p, '\0');
502 if (memmem (exts.c_str(), len, p, end - p) == NULL)
503 {
504 /* Failed to match this token. Turn off the
505 features we'd otherwise enable. */
506 enabled = false;
507 break;
508 }
509 if (*end == '\0')
510 break;
511 p = end + 1;
512 }
513 while (1);
514
515 if (enabled)
516 extension_flags |= aarch64_extensions[i].flag;
517 else
518 extension_flags &= ~(aarch64_extensions[i].flag);
519 }
520
521 processed_exts = true;
522 }
523 }
524 /* End of NetBSD specific section. */
525 #endif
526
527 /* Weird cpuinfo format that we don't know how to handle. */
528 if (n_cores == 0
529 || n_cores > 2
530 || (n_cores == 1 && n_variants != 1)
531 || imp == INVALID_IMP)
532 goto not_found;
533
534 /* Simple case, one core type or just looking for the arch. */
535 if (n_cores == 1 || arch)
536 {
537 /* Search for one of the cores in the list. */
538 for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
539 if (aarch64_cpu_data[i].implementer_id == imp
540 && cores[0] == aarch64_cpu_data[i].part_no
541 && (aarch64_cpu_data[i].variant == ALL_VARIANTS
542 || variants[0] == aarch64_cpu_data[i].variant))
543 break;
544 if (aarch64_cpu_data[i].name == NULL)
545 goto not_found;
546
547 if (arch)
548 {
549 const char *arch_id = aarch64_cpu_data[i].arch;
550 aarch64_arch_driver_info* arch_info = get_arch_from_id (arch_id);
551
552 /* We got some arch indentifier that's not in aarch64-arches.def? */
553 if (!arch_info)
554 goto not_found;
555
556 res = concat ("-march=", arch_info->name, NULL);
557 default_flags = arch_info->flags;
558 }
559 else
560 {
561 default_flags = aarch64_cpu_data[i].flags;
562 res = concat ("-m",
563 cpu ? "cpu" : "tune", "=",
564 aarch64_cpu_data[i].name,
565 NULL);
566 }
567 }
568 /* We have big.LITTLE. */
569 else
570 {
571 for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
572 {
573 if (aarch64_cpu_data[i].implementer_id == imp
574 && valid_bL_core_p (cores, aarch64_cpu_data[i].part_no))
575 {
576 res = concat ("-m",
577 cpu ? "cpu" : "tune", "=",
578 aarch64_cpu_data[i].name,
579 NULL);
580 default_flags = aarch64_cpu_data[i].flags;
581 break;
582 }
583 }
584 if (!res)
585 goto not_found;
586 }
587
588 if (tune)
589 return res;
590
591 {
592 std::string extension
593 = aarch64_get_extension_string_for_isa_flags (extension_flags,
594 default_flags);
595 res = concat (res, extension.c_str (), NULL);
596 }
597
598 return res;
599
600 not_found:
601 {
602 /* If detection fails we ignore the option.
603 Clean up and return NULL. */
604
605 if (f)
606 fclose (f);
607
608 return NULL;
609 }
610 }
611
612