1 /* Native CPU detection for aarch64.
2    Copyright (C) 2015-2018 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 #include "system.h"
25 #include "coretypes.h"
26 #include "tm.h"
27 
28 /* Defined in common/config/aarch64/aarch64-common.c.  */
29 std::string aarch64_get_extension_string_for_isa_flags (unsigned long,
30 							unsigned long);
31 
32 struct aarch64_arch_extension
33 {
34   const char *ext;
35   unsigned int flag;
36   const char *feat_string;
37 };
38 
39 #define AARCH64_OPT_EXTENSION(EXT_NAME, FLAG_CANONICAL, FLAGS_ON, FLAGS_OFF, \
40 			      SYNTHETIC, FEATURE_STRING) \
41   { EXT_NAME, FLAG_CANONICAL, FEATURE_STRING },
42 static struct aarch64_arch_extension aarch64_extensions[] =
43 {
44 #include "aarch64-option-extensions.def"
45 };
46 
47 
48 struct aarch64_core_data
49 {
50   const char* name;
51   const char* arch;
52   unsigned char implementer_id; /* Exactly 8 bits */
53   unsigned int part_no; /* 12 bits + 12 bits */
54   unsigned variant;
55   const unsigned long flags;
56 };
57 
58 #define AARCH64_BIG_LITTLE(BIG, LITTLE) \
59   (((BIG)&0xFFFu) << 12 | ((LITTLE) & 0xFFFu))
60 #define INVALID_IMP ((unsigned char) -1)
61 #define INVALID_CORE ((unsigned)-1)
62 #define ALL_VARIANTS ((unsigned)-1)
63 
64 #define AARCH64_CORE(CORE_NAME, CORE_IDENT, SCHED, ARCH, FLAGS, COSTS, IMP, PART, VARIANT) \
65   { CORE_NAME, #ARCH, IMP, PART, VARIANT, FLAGS },
66 
67 static struct aarch64_core_data aarch64_cpu_data[] =
68 {
69 #include "aarch64-cores.def"
70   { NULL, NULL, INVALID_IMP, INVALID_CORE, ALL_VARIANTS, 0 }
71 };
72 
73 
74 struct aarch64_arch_driver_info
75 {
76   const char* id;
77   const char* name;
78   const unsigned long flags;
79 };
80 
81 #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \
82   { #ARCH_IDENT, NAME, FLAGS },
83 
84 static struct aarch64_arch_driver_info aarch64_arches[] =
85 {
86 #include "aarch64-arches.def"
87   {NULL, NULL, 0}
88 };
89 
90 
91 /* Return an aarch64_arch_driver_info for the architecture described
92    by ID, or NULL if ID describes something we don't know about.  */
93 
94 static struct aarch64_arch_driver_info*
get_arch_from_id(const char * id)95 get_arch_from_id (const char* id)
96 {
97   unsigned int i = 0;
98 
99   for (i = 0; aarch64_arches[i].id != NULL; i++)
100     {
101       if (strcmp (id, aarch64_arches[i].id) == 0)
102 	return &aarch64_arches[i];
103     }
104 
105   return NULL;
106 }
107 
108 /* Check wether the CORE array is the same as the big.LITTLE BL_CORE.
109    For an example CORE={0xd08, 0xd03} and
110    BL_CORE=AARCH64_BIG_LITTLE (0xd08, 0xd03) will return true.  */
111 
112 static bool
valid_bL_core_p(unsigned int * core,unsigned int bL_core)113 valid_bL_core_p (unsigned int *core, unsigned int bL_core)
114 {
115   return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core
116          || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core;
117 }
118 
119 /* Returns the hex integer that is after ':' for the FIELD.
120    Returns -1 is returned if there was problem parsing the integer. */
121 static unsigned
parse_field(const char * field)122 parse_field (const char *field)
123 {
124   const char *rest = strchr (field, ':');
125   char *after;
126   unsigned fint = strtol (rest + 1, &after, 16);
127   if (after == rest + 1)
128     return -1;
129   return fint;
130 }
131 
132 /*  Return true iff ARR contains CORE, in either of the two elements. */
133 
134 static bool
contains_core_p(unsigned * arr,unsigned core)135 contains_core_p (unsigned *arr, unsigned core)
136 {
137   if (arr[0] != INVALID_CORE)
138     {
139       if (arr[0] == core)
140         return true;
141 
142       if (arr[1] != INVALID_CORE)
143         return arr[1] == core;
144     }
145 
146   return false;
147 }
148 
149 /* This will be called by the spec parser in gcc.c when it sees
150    a %:local_cpu_detect(args) construct.  Currently it will be called
151    with either "arch", "cpu" or "tune" as argument depending on if
152    -march=native, -mcpu=native or -mtune=native is to be substituted.
153 
154    It returns a string containing new command line parameters to be
155    put at the place of the above two options, depending on what CPU
156    this is executed.  E.g. "-march=armv8-a" on a Cortex-A57 for
157    -march=native.  If the routine can't detect a known processor,
158    the -march or -mtune option is discarded.
159 
160    For -mtune and -mcpu arguments it attempts to detect the CPU or
161    a big.LITTLE system.
162    ARGC and ARGV are set depending on the actual arguments given
163    in the spec.  */
164 
165 const char *
host_detect_local_cpu(int argc,const char ** argv)166 host_detect_local_cpu (int argc, const char **argv)
167 {
168   const char *res = NULL;
169   static const int num_exts = ARRAY_SIZE (aarch64_extensions);
170   char buf[128];
171   FILE *f = NULL;
172   bool arch = false;
173   bool tune = false;
174   bool cpu = false;
175   unsigned int i = 0;
176   unsigned char imp = INVALID_IMP;
177   unsigned int cores[2] = { INVALID_CORE, INVALID_CORE };
178   unsigned int n_cores = 0;
179   unsigned int variants[2] = { ALL_VARIANTS, ALL_VARIANTS };
180   unsigned int n_variants = 0;
181   bool processed_exts = false;
182   unsigned long extension_flags = 0;
183   unsigned long default_flags = 0;
184 
185   gcc_assert (argc);
186 
187   if (!argv[0])
188     goto not_found;
189 
190   /* Are we processing -march, mtune or mcpu?  */
191   arch = strcmp (argv[0], "arch") == 0;
192   if (!arch)
193     tune = strcmp (argv[0], "tune") == 0;
194 
195   if (!arch && !tune)
196     cpu = strcmp (argv[0], "cpu") == 0;
197 
198   if (!arch && !tune && !cpu)
199     goto not_found;
200 
201   f = fopen ("/proc/cpuinfo", "r");
202 
203   if (f == NULL)
204     goto not_found;
205 
206   /* Look through /proc/cpuinfo to determine the implementer
207      and then the part number that identifies a particular core.  */
208   while (fgets (buf, sizeof (buf), f) != NULL)
209     {
210       if (strstr (buf, "implementer") != NULL)
211 	{
212 	  unsigned cimp = parse_field (buf);
213 	  if (cimp == INVALID_IMP)
214 	    goto not_found;
215 
216 	  if (imp == INVALID_IMP)
217 	    imp = cimp;
218 	  /* FIXME: BIG.little implementers are always equal. */
219 	  else if (imp != cimp)
220 	    goto not_found;
221 	}
222 
223       if (strstr (buf, "variant") != NULL)
224 	{
225 	  unsigned cvariant = parse_field (buf);
226 	  if (!contains_core_p (variants, cvariant))
227 	    {
228               if (n_variants == 2)
229                 goto not_found;
230 
231               variants[n_variants++] = cvariant;
232 	    }
233           continue;
234         }
235 
236       if (strstr (buf, "part") != NULL)
237 	{
238 	  unsigned ccore = parse_field (buf);
239 	  if (!contains_core_p (cores, ccore))
240 	    {
241 	      if (n_cores == 2)
242 		goto not_found;
243 
244 	      cores[n_cores++] = ccore;
245 	    }
246 	  continue;
247 	}
248       if (!tune && !processed_exts && strstr (buf, "Features") != NULL)
249 	{
250 	  for (i = 0; i < num_exts; i++)
251 	    {
252 	      const char *p = aarch64_extensions[i].feat_string;
253 
254 	      /* If the feature contains no HWCAPS string then ignore it for the
255 		 auto detection.  */
256 	      if (*p == '\0')
257 		continue;
258 
259 	      bool enabled = true;
260 
261 	      /* This may be a multi-token feature string.  We need
262 		 to match all parts, which could be in any order.  */
263 	      size_t len = strlen (buf);
264 	      do
265 		{
266 		  const char *end = strchr (p, ' ');
267 		  if (end == NULL)
268 		    end = strchr (p, '\0');
269 		  if (memmem (buf, len, p, end - p) == NULL)
270 		    {
271 		      /* Failed to match this token.  Turn off the
272 			 features we'd otherwise enable.  */
273 		      enabled = false;
274 		      break;
275 		    }
276 		  if (*end == '\0')
277 		    break;
278 		  p = end + 1;
279 		}
280 	      while (1);
281 
282 	      if (enabled)
283 		extension_flags |= aarch64_extensions[i].flag;
284 	      else
285 		extension_flags &= ~(aarch64_extensions[i].flag);
286 	    }
287 
288 	  processed_exts = true;
289 	}
290     }
291 
292   fclose (f);
293   f = NULL;
294 
295   /* Weird cpuinfo format that we don't know how to handle.  */
296   if (n_cores == 0
297       || n_cores > 2
298       || (n_cores == 1 && n_variants != 1)
299       || imp == INVALID_IMP)
300     goto not_found;
301 
302   /* Simple case, one core type or just looking for the arch. */
303   if (n_cores == 1 || arch)
304     {
305       /* Search for one of the cores in the list. */
306       for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
307 	if (aarch64_cpu_data[i].implementer_id == imp
308             && cores[0] == aarch64_cpu_data[i].part_no
309             && (aarch64_cpu_data[i].variant == ALL_VARIANTS
310                 || variants[0] == aarch64_cpu_data[i].variant))
311 	  break;
312       if (aarch64_cpu_data[i].name == NULL)
313         goto not_found;
314 
315       if (arch)
316 	{
317 	  const char *arch_id = aarch64_cpu_data[i].arch;
318 	  aarch64_arch_driver_info* arch_info = get_arch_from_id (arch_id);
319 
320 	  /* We got some arch indentifier that's not in aarch64-arches.def?  */
321 	  if (!arch_info)
322 	    goto not_found;
323 
324 	  res = concat ("-march=", arch_info->name, NULL);
325 	  default_flags = arch_info->flags;
326 	}
327       else
328 	{
329 	  default_flags = aarch64_cpu_data[i].flags;
330 	  res = concat ("-m",
331 			cpu ? "cpu" : "tune", "=",
332 			aarch64_cpu_data[i].name,
333 			NULL);
334 	}
335     }
336   /* We have big.LITTLE.  */
337   else
338     {
339       for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
340 	{
341 	  if (aarch64_cpu_data[i].implementer_id == imp
342 	      && valid_bL_core_p (cores, aarch64_cpu_data[i].part_no))
343 	    {
344 	      res = concat ("-m",
345 			    cpu ? "cpu" : "tune", "=",
346 			    aarch64_cpu_data[i].name,
347 			    NULL);
348 	      default_flags = aarch64_cpu_data[i].flags;
349 	      break;
350 	    }
351 	}
352       if (!res)
353 	goto not_found;
354     }
355 
356   if (tune)
357     return res;
358 
359   {
360     std::string extension
361       = aarch64_get_extension_string_for_isa_flags (extension_flags,
362 						    default_flags);
363     res = concat (res, extension.c_str (), NULL);
364   }
365 
366   return res;
367 
368 not_found:
369   {
370    /* If detection fails we ignore the option.
371       Clean up and return NULL.  */
372 
373     if (f)
374       fclose (f);
375 
376     return NULL;
377   }
378 }
379 
380