xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/config/aarch64/driver-aarch64.c (revision 23f5f46327e37e7811da3520f4bb933f9489322f)
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