1 /* Native CPU detection for aarch64.
2    Copyright (C) 2015-2021 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 "aarch64-protos.h"
29 
30 struct aarch64_arch_extension
31 {
32   const char *ext;
33   uint64_t flag;
34   const char *feat_string;
35 };
36 
37 #define AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, \
38 			      SYNTHETIC, FEATURE_STRING) \
39   { EXT_NAME, FLAG_CANONICAL, FEATURE_STRING },
40 static struct aarch64_arch_extension aarch64_extensions[] =
41 {
42 #include "aarch64-option-extensions.def"
43 };
44 
45 
46 struct aarch64_core_data
47 {
48   const char* name;
49   const char* arch;
50   unsigned char implementer_id; /* Exactly 8 bits */
51   unsigned int part_no; /* 12 bits + 12 bits */
52   unsigned variant;
53   const uint64_t flags;
54 };
55 
56 #define AARCH64_BIG_LITTLE(BIG, LITTLE) \
57   (((BIG)&0xFFFu) << 12 | ((LITTLE) & 0xFFFu))
58 #define INVALID_IMP ((unsigned char) -1)
59 #define INVALID_CORE ((unsigned)-1)
60 #define ALL_VARIANTS ((unsigned)-1)
61 
62 #define AARCH64_CORE(CORE_NAME, CORE_IDENT, SCHED, ARCH, FLAGS, COSTS, IMP, PART, VARIANT) \
63   { CORE_NAME, #ARCH, IMP, PART, VARIANT, FLAGS },
64 
65 static struct aarch64_core_data aarch64_cpu_data[] =
66 {
67 #include "aarch64-cores.def"
68   { NULL, NULL, INVALID_IMP, INVALID_CORE, ALL_VARIANTS, 0 }
69 };
70 
71 
72 struct aarch64_arch_driver_info
73 {
74   const char* id;
75   const char* name;
76   const uint64_t flags;
77 };
78 
79 #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \
80   { #ARCH_IDENT, NAME, FLAGS },
81 
82 static struct aarch64_arch_driver_info aarch64_arches[] =
83 {
84 #include "aarch64-arches.def"
85   {NULL, NULL, 0}
86 };
87 
88 
89 /* Return an aarch64_arch_driver_info for the architecture described
90    by ID, or NULL if ID describes something we don't know about.  */
91 
92 static struct aarch64_arch_driver_info*
get_arch_from_id(const char * id)93 get_arch_from_id (const char* id)
94 {
95   unsigned int i = 0;
96 
97   for (i = 0; aarch64_arches[i].id != NULL; i++)
98     {
99       if (strcmp (id, aarch64_arches[i].id) == 0)
100 	return &aarch64_arches[i];
101     }
102 
103   return NULL;
104 }
105 
106 /* Check wether the CORE array is the same as the big.LITTLE BL_CORE.
107    For an example CORE={0xd08, 0xd03} and
108    BL_CORE=AARCH64_BIG_LITTLE (0xd08, 0xd03) will return true.  */
109 
110 static bool
valid_bL_core_p(unsigned int * core,unsigned int bL_core)111 valid_bL_core_p (unsigned int *core, unsigned int bL_core)
112 {
113   return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core
114          || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core;
115 }
116 
117 /* Returns the hex integer that is after ':' for the FIELD.
118    Returns -1 is returned if there was problem parsing the integer. */
119 static unsigned
parse_field(const std::string & field)120 parse_field (const std::string &field)
121 {
122   const char *rest = strchr (field.c_str (), ':');
123 
124   /* The line must be in the format of <name>:<value>, if it's not
125      then we have a weird format.  */
126   if (rest == NULL)
127     return -1;
128 
129   char *after;
130   unsigned fint = strtol (rest + 1, &after, 16);
131   if (after == rest + 1)
132     return -1;
133   return fint;
134 }
135 
136 /* Returns the index of the ':' inside the FIELD which must be found
137    after the value of KEY.  Returns string::npos if line does not contain
138    a field.  */
139 
140 static size_t
find_field(const std::string & field,const std::string & key)141 find_field (const std::string &field, const std::string &key)
142 {
143   size_t key_pos, sep_pos;
144   key_pos = field.find (key);
145   if (key_pos == std::string::npos)
146     return std::string::npos;
147 
148   sep_pos = field.find (":", key_pos + 1);
149   if (sep_pos == std::string::npos)
150     return std::string::npos;
151 
152   return sep_pos;
153 }
154 
155 /* Splits and returns a string based on whitespace and return it as
156    part of a set. Empty strings are ignored.  */
157 
158 static void
split_words(const std::string & val,std::set<std::string> & result)159 split_words (const std::string &val, std::set<std::string> &result)
160 {
161   size_t cur, prev = 0;
162   std::string word;
163   while ((cur = val.find_first_of (" \n", prev)) != std::string::npos)
164     {
165       word = val.substr (prev, cur - prev);
166       /* Skip adding empty words.  */
167       if (!word.empty ())
168 	result.insert (word);
169       prev = cur + 1;
170     }
171 
172   if (prev != cur)
173     result.insert (val.substr (prev));
174 }
175 
176 /* Read an entire line from F until '\n' or EOF.  */
177 
178 static std::string
readline(FILE * f)179 readline (FILE *f)
180 {
181   char *buf = NULL;
182   int size = 0;
183   int last = 0;
184   const int buf_size = 128;
185 
186   if (feof (f))
187     return std::string ();
188 
189   do
190     {
191       size += buf_size;
192       buf = (char*) xrealloc (buf, size);
193       gcc_assert (buf);
194       /* If fgets fails it returns NULL, but if it reaches EOF
195 	 with 0 characters read it also returns EOF.  However
196 	 the condition on the loop would have broken out of the
197 	 loop in that case,  and if we are in the first iteration
198 	 then the empty string is the correct thing to return.  */
199       if (!fgets (buf + last, buf_size, f))
200 	return std::string ();
201       /* If we're not at the end of the line then override the
202 	 \0 added by fgets.  */
203       last = strnlen (buf, size) - 1;
204     }
205   while (!feof (f) && buf[last] != '\n');
206 
207   std::string result (buf);
208   free (buf);
209   return result;
210 }
211 
212 /*  Return true iff ARR contains CORE, in either of the two elements. */
213 
214 static bool
contains_core_p(unsigned * arr,unsigned core)215 contains_core_p (unsigned *arr, unsigned core)
216 {
217   if (arr[0] != INVALID_CORE)
218     {
219       if (arr[0] == core)
220         return true;
221 
222       if (arr[1] != INVALID_CORE)
223         return arr[1] == core;
224     }
225 
226   return false;
227 }
228 
229 /* This will be called by the spec parser in gcc.c when it sees
230    a %:local_cpu_detect(args) construct.  Currently it will be called
231    with either "arch", "cpu" or "tune" as argument depending on if
232    -march=native, -mcpu=native or -mtune=native is to be substituted.
233 
234    It returns a string containing new command line parameters to be
235    put at the place of the above two options, depending on what CPU
236    this is executed.  E.g. "-march=armv8-a" on a Cortex-A57 for
237    -march=native.  If the routine can't detect a known processor,
238    the -march or -mtune option is discarded.
239 
240    For -mtune and -mcpu arguments it attempts to detect the CPU or
241    a big.LITTLE system.
242    ARGC and ARGV are set depending on the actual arguments given
243    in the spec.  */
244 
245 const char *
host_detect_local_cpu(int argc,const char ** argv)246 host_detect_local_cpu (int argc, const char **argv)
247 {
248   const char *res = NULL;
249   static const int num_exts = ARRAY_SIZE (aarch64_extensions);
250   FILE *f = NULL;
251   bool arch = false;
252   bool tune = false;
253   bool cpu = false;
254   unsigned int i = 0;
255   unsigned char imp = INVALID_IMP;
256   unsigned int cores[2] = { INVALID_CORE, INVALID_CORE };
257   unsigned int n_cores = 0;
258   unsigned int variants[2] = { ALL_VARIANTS, ALL_VARIANTS };
259   unsigned int n_variants = 0;
260   bool processed_exts = false;
261   uint64_t extension_flags = 0;
262   uint64_t default_flags = 0;
263   std::string buf;
264   size_t sep_pos = -1;
265   char *fcpu_info;
266 
267   gcc_assert (argc);
268 
269   if (!argv[0])
270     goto not_found;
271 
272   /* Are we processing -march, mtune or mcpu?  */
273   arch = strcmp (argv[0], "arch") == 0;
274   if (!arch)
275     tune = strcmp (argv[0], "tune") == 0;
276 
277   if (!arch && !tune)
278     cpu = strcmp (argv[0], "cpu") == 0;
279 
280   if (!arch && !tune && !cpu)
281     goto not_found;
282 
283   fcpu_info = getenv ("GCC_CPUINFO");
284   if (fcpu_info)
285     f = fopen (fcpu_info, "r");
286   else
287     f = fopen ("/proc/cpuinfo", "r");
288 
289   if (f == NULL)
290     goto not_found;
291 
292   /* Look through /proc/cpuinfo to determine the implementer
293      and then the part number that identifies a particular core.  */
294   while (!(buf = readline (f)).empty ())
295     {
296       if (find_field (buf, "implementer") != std::string::npos)
297 	{
298 	  unsigned cimp = parse_field (buf);
299 	  if (cimp == INVALID_IMP)
300 	    goto not_found;
301 
302 	  if (imp == INVALID_IMP)
303 	    imp = cimp;
304 	  /* FIXME: BIG.little implementers are always equal. */
305 	  else if (imp != cimp)
306 	    goto not_found;
307 	}
308       else if (find_field (buf, "variant") != std::string::npos)
309 	{
310 	  unsigned cvariant = parse_field (buf);
311 	  if (!contains_core_p (variants, cvariant))
312 	    {
313               if (n_variants == 2)
314                 goto not_found;
315 
316               variants[n_variants++] = cvariant;
317 	    }
318           continue;
319         }
320       else if (find_field (buf, "part") != std::string::npos)
321 	{
322 	  unsigned ccore = parse_field (buf);
323 	  if (!contains_core_p (cores, ccore))
324 	    {
325 	      if (n_cores == 2)
326 		goto not_found;
327 
328 	      cores[n_cores++] = ccore;
329 	    }
330 	  continue;
331 	}
332       else if (!tune && !processed_exts
333 	       && (sep_pos = find_field (buf, "Features")) != std::string::npos)
334 	{
335 	  /* First create the list of features in the buffer.  */
336 	  std::set<std::string> features;
337 	  /* Drop everything till the :.  */
338 	  buf = buf.substr (sep_pos + 1);
339 	  split_words (buf, features);
340 
341 	  for (i = 0; i < num_exts; i++)
342 	    {
343 	      const std::string val (aarch64_extensions[i].feat_string);
344 
345 	      /* If the feature contains no HWCAPS string then ignore it for the
346 		 auto detection.  */
347 	      if (val.empty ())
348 		continue;
349 
350 	      bool enabled = true;
351 
352 	      /* This may be a multi-token feature string.  We need
353 		 to match all parts, which could be in any order.  */
354 	      std::set<std::string> tokens;
355 	      split_words (val, tokens);
356 	      std::set<std::string>::iterator it;
357 
358 	      /* Iterate till the first feature isn't found or all of them
359 		 are found.  */
360 	      for (it = tokens.begin (); enabled && it != tokens.end (); ++it)
361 		enabled = enabled && features.count (*it);
362 
363 	      if (enabled)
364 		extension_flags |= aarch64_extensions[i].flag;
365 	      else
366 		extension_flags &= ~(aarch64_extensions[i].flag);
367 	    }
368 
369 	  processed_exts = true;
370 	}
371     }
372 
373   fclose (f);
374   f = NULL;
375 
376   /* Weird cpuinfo format that we don't know how to handle.  */
377   if (n_cores == 0
378       || n_cores > 2
379       || (n_cores == 1 && n_variants != 1)
380       || imp == INVALID_IMP)
381     goto not_found;
382 
383   /* Simple case, one core type or just looking for the arch. */
384   if (n_cores == 1 || arch)
385     {
386       /* Search for one of the cores in the list. */
387       for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
388 	if (aarch64_cpu_data[i].implementer_id == imp
389             && cores[0] == aarch64_cpu_data[i].part_no
390             && (aarch64_cpu_data[i].variant == ALL_VARIANTS
391                 || variants[0] == aarch64_cpu_data[i].variant))
392 	  break;
393       if (aarch64_cpu_data[i].name == NULL)
394         goto not_found;
395 
396       if (arch)
397 	{
398 	  const char *arch_id = aarch64_cpu_data[i].arch;
399 	  aarch64_arch_driver_info* arch_info = get_arch_from_id (arch_id);
400 
401 	  /* We got some arch indentifier that's not in aarch64-arches.def?  */
402 	  if (!arch_info)
403 	    goto not_found;
404 
405 	  res = concat ("-march=", arch_info->name, NULL);
406 	  default_flags = arch_info->flags;
407 	}
408       else
409 	{
410 	  default_flags = aarch64_cpu_data[i].flags;
411 	  res = concat ("-m",
412 			cpu ? "cpu" : "tune", "=",
413 			aarch64_cpu_data[i].name,
414 			NULL);
415 	}
416     }
417   /* We have big.LITTLE.  */
418   else
419     {
420       for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
421 	{
422 	  if (aarch64_cpu_data[i].implementer_id == imp
423 	      && valid_bL_core_p (cores, aarch64_cpu_data[i].part_no))
424 	    {
425 	      res = concat ("-m",
426 			    cpu ? "cpu" : "tune", "=",
427 			    aarch64_cpu_data[i].name,
428 			    NULL);
429 	      default_flags = aarch64_cpu_data[i].flags;
430 	      break;
431 	    }
432 	}
433       if (!res)
434 	goto not_found;
435     }
436 
437   if (tune)
438     return res;
439 
440   {
441     std::string extension
442       = aarch64_get_extension_string_for_isa_flags (extension_flags,
443 						    default_flags);
444     res = concat (res, extension.c_str (), NULL);
445   }
446 
447   return res;
448 
449 not_found:
450   {
451    /* If detection fails we ignore the option.
452       Clean up and return NULL.  */
453 
454     if (f)
455       fclose (f);
456 
457     return NULL;
458   }
459 }
460 
461