Daily bump.
[official-gcc.git] / gcc / config / aarch64 / driver-aarch64.cc
blob9d99554dbc28695865380563b1ab03979d947d67
1 /* Native CPU detection for aarch64.
2 Copyright (C) 2015-2025 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
20 #define IN_TARGET_CODE 1
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 #include "aarch64-feature-deps.h"
31 struct aarch64_arch_extension
33 const char *ext;
34 aarch64_feature_flags flag;
35 const char *feat_string;
38 #define AARCH64_OPT_EXTENSION(EXT_NAME, IDENT, C, D, E, FEATURE_STRING) \
39 { EXT_NAME, AARCH64_FL_##IDENT, FEATURE_STRING },
40 static struct aarch64_arch_extension aarch64_extensions[] =
42 #include "aarch64-option-extensions.def"
46 struct aarch64_core_data
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 aarch64_feature_flags flags;
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 /* Default architecture to use if -mcpu=native did not detect a known CPU. */
62 #define DEFAULT_ARCH "8A"
63 #define DEFAULT_CPU "generic-armv8-a"
65 #define AARCH64_CORE(CORE_NAME, CORE_IDENT, SCHED, ARCH, FLAGS, COSTS, IMP, PART, VARIANT) \
66 { CORE_NAME, #ARCH, IMP, PART, VARIANT, feature_deps::cpu_##CORE_IDENT },
68 static CONSTEXPR const aarch64_core_data aarch64_cpu_data[] =
70 #include "aarch64-cores.def"
71 { NULL, NULL, INVALID_IMP, INVALID_CORE, ALL_VARIANTS, 0 }
75 struct aarch64_arch_driver_info
77 const char* id;
78 const char* name;
79 aarch64_feature_flags flags;
82 /* Skip the leading "V" in the architecture name. */
83 #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \
84 { #ARCH_IDENT + 1, NAME, feature_deps::ARCH_IDENT ().enable },
86 static CONSTEXPR const aarch64_arch_driver_info aarch64_arches[] =
88 #include "aarch64-arches.def"
89 {NULL, NULL, 0}
93 /* Return an aarch64_arch_driver_info for the architecture described
94 by ID, or NULL if ID describes something we don't know about. */
96 static const aarch64_arch_driver_info *
97 get_arch_from_id (const char* id)
99 unsigned int i = 0;
101 for (i = 0; aarch64_arches[i].id != NULL; i++)
103 if (strcmp (id, aarch64_arches[i].id) == 0)
104 return &aarch64_arches[i];
107 return NULL;
110 /* Return an aarch64_core_data for the cpu described
111 by ID, or NULL if ID describes something we don't know about. */
113 static const aarch64_core_data *
114 get_cpu_from_id (const char* name)
116 for (unsigned i = 0; aarch64_cpu_data[i].name != NULL; i++)
117 if (strcmp (name, aarch64_cpu_data[i].name) == 0)
118 return &aarch64_cpu_data[i];
120 return NULL;
123 /* Check wether the CORE array is the same as the big.LITTLE BL_CORE.
124 For an example CORE={0xd08, 0xd03} and
125 BL_CORE=AARCH64_BIG_LITTLE (0xd08, 0xd03) will return true. */
127 static bool
128 valid_bL_core_p (unsigned int *core, unsigned int bL_core)
130 return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core
131 || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core;
134 /* Returns the hex integer that is after ':' for the FIELD.
135 Returns -1 is returned if there was problem parsing the integer. */
136 static unsigned
137 parse_field (const std::string &field)
139 const char *rest = strchr (field.c_str (), ':');
141 /* The line must be in the format of <name>:<value>, if it's not
142 then we have a weird format. */
143 if (rest == NULL)
144 return -1;
146 char *after;
147 unsigned fint = strtol (rest + 1, &after, 16);
148 if (after == rest + 1)
149 return -1;
150 return fint;
153 /* Returns the index of the ':' inside the FIELD which must be found
154 after the value of KEY. Returns string::npos if line does not contain
155 a field. */
157 static size_t
158 find_field (const std::string &field, const std::string &key)
160 size_t key_pos, sep_pos;
161 key_pos = field.find (key);
162 if (key_pos == std::string::npos)
163 return std::string::npos;
165 sep_pos = field.find (":", key_pos + 1);
166 if (sep_pos == std::string::npos)
167 return std::string::npos;
169 return sep_pos;
172 /* Splits and returns a string based on whitespace and return it as
173 part of a set. Empty strings are ignored. */
175 static void
176 split_words (const std::string &val, std::set<std::string> &result)
178 size_t cur, prev = 0;
179 std::string word;
180 while ((cur = val.find_first_of (" \n", prev)) != std::string::npos)
182 word = val.substr (prev, cur - prev);
183 /* Skip adding empty words. */
184 if (!word.empty ())
185 result.insert (word);
186 prev = cur + 1;
189 if (prev != cur)
190 result.insert (val.substr (prev));
193 /* Read an entire line from F until '\n' or EOF. */
195 static std::string
196 readline (FILE *f)
198 char *buf = NULL;
199 int size = 0;
200 int last = 0;
201 const int buf_size = 128;
203 if (feof (f))
204 return std::string ();
208 size += buf_size;
209 buf = (char*) xrealloc (buf, size);
210 gcc_assert (buf);
211 /* If fgets fails it returns NULL, but if it reaches EOF
212 with 0 characters read it also returns EOF. However
213 the condition on the loop would have broken out of the
214 loop in that case, and if we are in the first iteration
215 then the empty string is the correct thing to return. */
216 if (!fgets (buf + last, buf_size, f))
217 return std::string ();
218 /* If we're not at the end of the line then override the
219 \0 added by fgets. */
220 last = strnlen (buf, size);
222 while (!feof (f) && last > 0 && buf[last - 1] != '\n');
224 std::string result (buf);
225 free (buf);
226 return result;
229 /* Return true iff ARR contains CORE, in either of the two elements. */
231 static bool
232 contains_core_p (unsigned *arr, unsigned core)
234 if (arr[0] != INVALID_CORE)
236 if (arr[0] == core)
237 return true;
239 if (arr[1] != INVALID_CORE)
240 return arr[1] == core;
243 return false;
246 /* This will be called by the spec parser in gcc.cc when it sees
247 a %:local_cpu_detect(args) construct. Currently it will be called
248 with either "arch", "cpu" or "tune" as argument depending on if
249 -march=native, -mcpu=native or -mtune=native is to be substituted.
251 It returns a string containing new command line parameters to be
252 put at the place of the above two options, depending on what CPU
253 this is executed. E.g. "-march=armv8-a" on a Cortex-A57 for
254 -march=native. If the routine can't detect a known processor,
255 the -march or -mtune option is discarded.
257 For -mtune and -mcpu arguments it attempts to detect the CPU or
258 a big.LITTLE system.
259 ARGC and ARGV are set depending on the actual arguments given
260 in the spec. */
262 const char *
263 host_detect_local_cpu (int argc, const char **argv)
265 const char *res = NULL;
266 static const int num_exts = ARRAY_SIZE (aarch64_extensions);
267 FILE *f = NULL;
268 bool arch = false;
269 bool tune = false;
270 bool cpu = false;
271 unsigned int i = 0;
272 unsigned char imp = INVALID_IMP;
273 unsigned int cores[3] = { INVALID_CORE, INVALID_CORE, INVALID_CORE };
274 unsigned int n_cores = 0;
275 unsigned int variants[3] = { ALL_VARIANTS, ALL_VARIANTS, ALL_VARIANTS };
276 unsigned int n_variants = 0;
277 bool processed_exts = false;
278 aarch64_feature_flags extension_flags = 0;
279 aarch64_feature_flags unchecked_extension_flags = 0;
280 aarch64_feature_flags default_flags = 0;
281 std::string buf;
282 size_t sep_pos = -1;
283 char *fcpu_info;
285 gcc_assert (argc);
287 if (!argv[0])
288 goto not_found;
290 /* Are we processing -march, mtune or mcpu? */
291 arch = strcmp (argv[0], "arch") == 0;
292 if (!arch)
293 tune = strcmp (argv[0], "tune") == 0;
295 if (!arch && !tune)
296 cpu = strcmp (argv[0], "cpu") == 0;
298 if (!arch && !tune && !cpu)
299 goto not_found;
301 fcpu_info = getenv ("GCC_CPUINFO");
302 if (fcpu_info)
303 f = fopen (fcpu_info, "r");
304 else
305 f = fopen ("/proc/cpuinfo", "r");
307 if (f == NULL)
308 goto not_found;
310 /* Look through /proc/cpuinfo to determine the implementer
311 and then the part number that identifies a particular core. */
312 while (!(buf = readline (f)).empty ())
314 if (find_field (buf, "implementer") != std::string::npos)
316 unsigned cimp = parse_field (buf);
317 if (cimp == INVALID_IMP)
318 goto not_found;
320 if (imp == INVALID_IMP)
321 imp = cimp;
322 /* FIXME: BIG.little implementers are always equal. */
323 else if (imp != cimp)
324 goto not_found;
326 else if (find_field (buf, "variant") != std::string::npos)
328 unsigned cvariant = parse_field (buf);
329 if (!contains_core_p (variants, cvariant))
331 if (n_variants == 3)
332 goto not_found;
334 variants[n_variants++] = cvariant;
336 continue;
338 else if (find_field (buf, "part") != std::string::npos)
340 unsigned ccore = parse_field (buf);
341 if (!contains_core_p (cores, ccore))
343 if (n_cores == 3)
344 goto not_found;
346 cores[n_cores++] = ccore;
348 continue;
350 else if (!tune && !processed_exts
351 && (sep_pos = find_field (buf, "Features")) != std::string::npos)
353 /* First create the list of features in the buffer. */
354 std::set<std::string> features;
355 /* Drop everything till the :. */
356 buf = buf.substr (sep_pos + 1);
357 split_words (buf, features);
359 for (i = 0; i < num_exts; i++)
361 const std::string val (aarch64_extensions[i].feat_string);
363 /* If the feature contains no HWCAPS string then ignore it for the
364 auto detection. */
365 if (val.empty ())
367 unchecked_extension_flags |= aarch64_extensions[i].flag;
368 continue;
371 bool enabled = true;
373 /* This may be a multi-token feature string. We need
374 to match all parts, which could be in any order. */
375 std::set<std::string> tokens;
376 split_words (val, tokens);
377 std::set<std::string>::iterator it;
379 /* Iterate till the first feature isn't found or all of them
380 are found. */
381 for (it = tokens.begin (); enabled && it != tokens.end (); ++it)
382 enabled = enabled && features.count (*it);
384 if (enabled)
385 extension_flags |= aarch64_extensions[i].flag;
386 else
387 extension_flags &= ~(aarch64_extensions[i].flag);
390 processed_exts = true;
394 fclose (f);
395 f = NULL;
397 /* Weird cpuinfo format that we don't know how to handle. */
398 if (n_cores == 0
399 || n_cores > 2
400 || imp == INVALID_IMP
401 || !processed_exts)
402 goto not_found;
404 /* If we have one core type but multiple variants, consider
405 that as one variant with ALL_VARIANTS instead. */
406 if (n_cores == 1 && n_variants != 1)
407 variants[0] = ALL_VARIANTS;
409 /* Simple case, one core type or just looking for the arch. */
410 if (n_cores == 1 || arch)
412 /* Search for one of the cores in the list. */
413 for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
414 if (aarch64_cpu_data[i].implementer_id == imp
415 && cores[0] == aarch64_cpu_data[i].part_no
416 && (aarch64_cpu_data[i].variant == ALL_VARIANTS
417 || variants[0] == aarch64_cpu_data[i].variant))
418 break;
420 if (arch)
422 const char *arch_id = (aarch64_cpu_data[i].name
423 ? aarch64_cpu_data[i].arch
424 : DEFAULT_ARCH);
425 auto arch_info = get_arch_from_id (arch_id);
427 /* We got some arch indentifier that's not in aarch64-arches.def? */
428 if (!arch_info)
429 goto not_found;
431 res = concat ("-march=", arch_info->name, NULL);
432 default_flags = arch_info->flags;
434 else if (cpu || aarch64_cpu_data[i].name)
436 auto cpu_info = (aarch64_cpu_data[i].name
437 ? &aarch64_cpu_data[i]
438 : get_cpu_from_id (DEFAULT_CPU));
439 default_flags = cpu_info->flags;
440 res = concat ("-m",
441 cpu ? "cpu" : "tune", "=",
442 cpu_info->name,
443 NULL);
446 /* We have big.LITTLE. */
447 else
449 for (i = 0; aarch64_cpu_data[i].name != NULL; i++)
451 if (aarch64_cpu_data[i].implementer_id == imp
452 && valid_bL_core_p (cores, aarch64_cpu_data[i].part_no))
454 res = concat ("-m",
455 cpu ? "cpu" : "tune", "=",
456 aarch64_cpu_data[i].name,
457 NULL);
458 default_flags = aarch64_cpu_data[i].flags;
459 break;
463 /* On big.LITTLE if we find any unknown CPUs we can still pick arch
464 features as the cores should have the same features. So just pick
465 the feature flags from any of the cpus. */
466 if (cpu && aarch64_cpu_data[i].name == NULL)
468 auto cpu_info = get_cpu_from_id (DEFAULT_CPU);
470 gcc_assert (cpu_info);
472 res = concat ("-mcpu=", cpu_info->name, NULL);
473 default_flags = cpu_info->flags;
476 if (!res)
477 goto not_found;
480 if (tune)
481 return res;
483 /* Add any features that should be present, but can't be verified using
484 the /proc/cpuinfo "Features" list. */
485 extension_flags |= unchecked_extension_flags & default_flags;
488 std::string extension
489 = aarch64_get_extension_string_for_isa_flags (extension_flags,
490 default_flags);
491 res = concat (res, extension.c_str (), NULL);
494 return res;
496 not_found:
498 /* If detection fails we ignore the option.
499 Clean up and return NULL. */
501 if (f)
502 fclose (f);
504 return NULL;