]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/aarch64/driver-aarch64.c
Update copyright years.
[thirdparty/gcc.git] / gcc / config / aarch64 / driver-aarch64.c
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*
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
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
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
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
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
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
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 *
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