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