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