]>
Commit | Line | Data |
---|---|---|
7e1bcce3 | 1 | /* Native CPU detection for aarch64. |
7adcbafe | 2 | Copyright (C) 2015-2022 Free Software Foundation, Inc. |
7e1bcce3 KT |
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 | ||
8fcc61f8 RS |
20 | #define IN_TARGET_CODE 1 |
21 | ||
7e1bcce3 | 22 | #include "config.h" |
8433cb91 | 23 | #define INCLUDE_STRING |
b399f3c6 | 24 | #define INCLUDE_SET |
7e1bcce3 | 25 | #include "system.h" |
8433cb91 JG |
26 | #include "coretypes.h" |
27 | #include "tm.h" | |
cb5ecbc4 | 28 | #include "aarch64-protos.h" |
11a113d5 | 29 | #include "aarch64-feature-deps.h" |
8433cb91 JG |
30 | |
31 | struct aarch64_arch_extension | |
7e1bcce3 KT |
32 | { |
33 | const char *ext; | |
fed55a60 | 34 | aarch64_feature_flags flag; |
7e1bcce3 KT |
35 | const char *feat_string; |
36 | }; | |
37 | ||
11a113d5 RS |
38 | #define AARCH64_OPT_EXTENSION(EXT_NAME, IDENT, C, D, E, FEATURE_STRING) \ |
39 | { EXT_NAME, AARCH64_FL_##IDENT, FEATURE_STRING }, | |
8433cb91 | 40 | static struct aarch64_arch_extension aarch64_extensions[] = |
7e1bcce3 KT |
41 | { |
42 | #include "aarch64-option-extensions.def" | |
43 | }; | |
7e1bcce3 KT |
44 | |
45 | ||
46 | struct aarch64_core_data | |
47 | { | |
48 | const char* name; | |
49 | const char* arch; | |
2e721daa AP |
50 | unsigned char implementer_id; /* Exactly 8 bits */ |
51 | unsigned int part_no; /* 12 bits + 12 bits */ | |
e8fcc9fa | 52 | unsigned variant; |
fed55a60 | 53 | aarch64_feature_flags flags; |
7e1bcce3 KT |
54 | }; |
55 | ||
2e721daa AP |
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) | |
e8fcc9fa | 60 | #define ALL_VARIANTS ((unsigned)-1) |
e91a17fe TC |
61 | /* Default architecture to use if -mcpu=native did not detect a known CPU. */ |
62 | #define DEFAULT_ARCH "8A" | |
2e721daa | 63 | |
e8fcc9fa | 64 | #define AARCH64_CORE(CORE_NAME, CORE_IDENT, SCHED, ARCH, FLAGS, COSTS, IMP, PART, VARIANT) \ |
11a113d5 | 65 | { CORE_NAME, #ARCH, IMP, PART, VARIANT, feature_deps::cpu_##CORE_IDENT }, |
7e1bcce3 | 66 | |
f95d3d5d | 67 | static CONSTEXPR const aarch64_core_data aarch64_cpu_data[] = |
7e1bcce3 KT |
68 | { |
69 | #include "aarch64-cores.def" | |
e8fcc9fa | 70 | { NULL, NULL, INVALID_IMP, INVALID_CORE, ALL_VARIANTS, 0 } |
7e1bcce3 KT |
71 | }; |
72 | ||
7e1bcce3 | 73 | |
393ae126 | 74 | struct aarch64_arch_driver_info |
7e1bcce3 KT |
75 | { |
76 | const char* id; | |
77 | const char* name; | |
fed55a60 | 78 | aarch64_feature_flags flags; |
7e1bcce3 KT |
79 | }; |
80 | ||
00c22ba6 | 81 | /* Skip the leading "V" in the architecture name. */ |
393ae126 | 82 | #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \ |
11a113d5 | 83 | { #ARCH_IDENT + 1, NAME, feature_deps::ARCH_IDENT ().enable }, |
7e1bcce3 | 84 | |
f95d3d5d | 85 | static CONSTEXPR const aarch64_arch_driver_info aarch64_arches[] = |
7e1bcce3 KT |
86 | { |
87 | #include "aarch64-arches.def" | |
8433cb91 | 88 | {NULL, NULL, 0} |
7e1bcce3 KT |
89 | }; |
90 | ||
7e1bcce3 | 91 | |
8433cb91 JG |
92 | /* Return an aarch64_arch_driver_info for the architecture described |
93 | by ID, or NULL if ID describes something we don't know about. */ | |
7e1bcce3 | 94 | |
60dee638 | 95 | static const aarch64_arch_driver_info * |
8433cb91 | 96 | get_arch_from_id (const char* id) |
7e1bcce3 KT |
97 | { |
98 | unsigned int i = 0; | |
99 | ||
100 | for (i = 0; aarch64_arches[i].id != NULL; i++) | |
101 | { | |
102 | if (strcmp (id, aarch64_arches[i].id) == 0) | |
8433cb91 | 103 | return &aarch64_arches[i]; |
7e1bcce3 KT |
104 | } |
105 | ||
106 | return NULL; | |
107 | } | |
108 | ||
2e721daa AP |
109 | /* Check wether the CORE array is the same as the big.LITTLE BL_CORE. |
110 | For an example CORE={0xd08, 0xd03} and | |
111 | BL_CORE=AARCH64_BIG_LITTLE (0xd08, 0xd03) will return true. */ | |
7e1bcce3 KT |
112 | |
113 | static bool | |
2e721daa AP |
114 | valid_bL_core_p (unsigned int *core, unsigned int bL_core) |
115 | { | |
116 | return AARCH64_BIG_LITTLE (core[0], core[1]) == bL_core | |
117 | || AARCH64_BIG_LITTLE (core[1], core[0]) == bL_core; | |
118 | } | |
119 | ||
120 | /* Returns the hex integer that is after ':' for the FIELD. | |
121 | Returns -1 is returned if there was problem parsing the integer. */ | |
122 | static unsigned | |
b399f3c6 | 123 | parse_field (const std::string &field) |
7e1bcce3 | 124 | { |
b399f3c6 TC |
125 | const char *rest = strchr (field.c_str (), ':'); |
126 | ||
127 | /* The line must be in the format of <name>:<value>, if it's not | |
128 | then we have a weird format. */ | |
129 | if (rest == NULL) | |
130 | return -1; | |
131 | ||
2e721daa AP |
132 | char *after; |
133 | unsigned fint = strtol (rest + 1, &after, 16); | |
134 | if (after == rest + 1) | |
135 | return -1; | |
136 | return fint; | |
7e1bcce3 KT |
137 | } |
138 | ||
b399f3c6 TC |
139 | /* Returns the index of the ':' inside the FIELD which must be found |
140 | after the value of KEY. Returns string::npos if line does not contain | |
141 | a field. */ | |
142 | ||
143 | static size_t | |
144 | find_field (const std::string &field, const std::string &key) | |
145 | { | |
146 | size_t key_pos, sep_pos; | |
147 | key_pos = field.find (key); | |
148 | if (key_pos == std::string::npos) | |
149 | return std::string::npos; | |
150 | ||
151 | sep_pos = field.find (":", key_pos + 1); | |
152 | if (sep_pos == std::string::npos) | |
153 | return std::string::npos; | |
154 | ||
155 | return sep_pos; | |
156 | } | |
157 | ||
158 | /* Splits and returns a string based on whitespace and return it as | |
159 | part of a set. Empty strings are ignored. */ | |
160 | ||
161 | static void | |
162 | split_words (const std::string &val, std::set<std::string> &result) | |
163 | { | |
164 | size_t cur, prev = 0; | |
165 | std::string word; | |
166 | while ((cur = val.find_first_of (" \n", prev)) != std::string::npos) | |
167 | { | |
168 | word = val.substr (prev, cur - prev); | |
169 | /* Skip adding empty words. */ | |
170 | if (!word.empty ()) | |
171 | result.insert (word); | |
172 | prev = cur + 1; | |
173 | } | |
174 | ||
175 | if (prev != cur) | |
176 | result.insert (val.substr (prev)); | |
177 | } | |
178 | ||
179 | /* Read an entire line from F until '\n' or EOF. */ | |
180 | ||
181 | static std::string | |
182 | readline (FILE *f) | |
183 | { | |
184 | char *buf = NULL; | |
185 | int size = 0; | |
186 | int last = 0; | |
187 | const int buf_size = 128; | |
188 | ||
189 | if (feof (f)) | |
190 | return std::string (); | |
191 | ||
192 | do | |
193 | { | |
194 | size += buf_size; | |
195 | buf = (char*) xrealloc (buf, size); | |
196 | gcc_assert (buf); | |
34157340 TC |
197 | /* If fgets fails it returns NULL, but if it reaches EOF |
198 | with 0 characters read it also returns EOF. However | |
199 | the condition on the loop would have broken out of the | |
200 | loop in that case, and if we are in the first iteration | |
201 | then the empty string is the correct thing to return. */ | |
202 | if (!fgets (buf + last, buf_size, f)) | |
203 | return std::string (); | |
b399f3c6 TC |
204 | /* If we're not at the end of the line then override the |
205 | \0 added by fgets. */ | |
b1cfbccc | 206 | last = strnlen (buf, size); |
b399f3c6 | 207 | } |
b1cfbccc | 208 | while (!feof (f) && last > 0 && buf[last - 1] != '\n'); |
b399f3c6 TC |
209 | |
210 | std::string result (buf); | |
211 | free (buf); | |
212 | return result; | |
213 | } | |
214 | ||
2e721daa | 215 | /* Return true iff ARR contains CORE, in either of the two elements. */ |
7e1bcce3 KT |
216 | |
217 | static bool | |
2e721daa | 218 | contains_core_p (unsigned *arr, unsigned core) |
7e1bcce3 | 219 | { |
2e721daa | 220 | if (arr[0] != INVALID_CORE) |
7e1bcce3 | 221 | { |
2e721daa AP |
222 | if (arr[0] == core) |
223 | return true; | |
7e1bcce3 | 224 | |
2e721daa AP |
225 | if (arr[1] != INVALID_CORE) |
226 | return arr[1] == core; | |
7e1bcce3 KT |
227 | } |
228 | ||
229 | return false; | |
230 | } | |
231 | ||
e53b6e56 | 232 | /* This will be called by the spec parser in gcc.cc when it sees |
7e1bcce3 KT |
233 | a %:local_cpu_detect(args) construct. Currently it will be called |
234 | with either "arch", "cpu" or "tune" as argument depending on if | |
235 | -march=native, -mcpu=native or -mtune=native is to be substituted. | |
236 | ||
237 | It returns a string containing new command line parameters to be | |
238 | put at the place of the above two options, depending on what CPU | |
239 | this is executed. E.g. "-march=armv8-a" on a Cortex-A57 for | |
240 | -march=native. If the routine can't detect a known processor, | |
241 | the -march or -mtune option is discarded. | |
242 | ||
243 | For -mtune and -mcpu arguments it attempts to detect the CPU or | |
244 | a big.LITTLE system. | |
245 | ARGC and ARGV are set depending on the actual arguments given | |
246 | in the spec. */ | |
247 | ||
248 | const char * | |
249 | host_detect_local_cpu (int argc, const char **argv) | |
250 | { | |
7e1bcce3 | 251 | const char *res = NULL; |
8433cb91 | 252 | static const int num_exts = ARRAY_SIZE (aarch64_extensions); |
7e1bcce3 KT |
253 | FILE *f = NULL; |
254 | bool arch = false; | |
255 | bool tune = false; | |
256 | bool cpu = false; | |
257 | unsigned int i = 0; | |
2e721daa AP |
258 | unsigned char imp = INVALID_IMP; |
259 | unsigned int cores[2] = { INVALID_CORE, INVALID_CORE }; | |
7e1bcce3 | 260 | unsigned int n_cores = 0; |
e8fcc9fa AP |
261 | unsigned int variants[2] = { ALL_VARIANTS, ALL_VARIANTS }; |
262 | unsigned int n_variants = 0; | |
7e1bcce3 | 263 | bool processed_exts = false; |
fed55a60 RS |
264 | aarch64_feature_flags extension_flags = 0; |
265 | aarch64_feature_flags default_flags = 0; | |
b399f3c6 TC |
266 | std::string buf; |
267 | size_t sep_pos = -1; | |
55f6addc | 268 | char *fcpu_info; |
7e1bcce3 KT |
269 | |
270 | gcc_assert (argc); | |
271 | ||
272 | if (!argv[0]) | |
273 | goto not_found; | |
274 | ||
275 | /* Are we processing -march, mtune or mcpu? */ | |
276 | arch = strcmp (argv[0], "arch") == 0; | |
277 | if (!arch) | |
278 | tune = strcmp (argv[0], "tune") == 0; | |
279 | ||
280 | if (!arch && !tune) | |
281 | cpu = strcmp (argv[0], "cpu") == 0; | |
282 | ||
283 | if (!arch && !tune && !cpu) | |
284 | goto not_found; | |
285 | ||
55f6addc TC |
286 | fcpu_info = getenv ("GCC_CPUINFO"); |
287 | if (fcpu_info) | |
288 | f = fopen (fcpu_info, "r"); | |
289 | else | |
290 | f = fopen ("/proc/cpuinfo", "r"); | |
7e1bcce3 KT |
291 | |
292 | if (f == NULL) | |
293 | goto not_found; | |
294 | ||
295 | /* Look through /proc/cpuinfo to determine the implementer | |
296 | and then the part number that identifies a particular core. */ | |
b399f3c6 | 297 | while (!(buf = readline (f)).empty ()) |
7e1bcce3 | 298 | { |
b399f3c6 | 299 | if (find_field (buf, "implementer") != std::string::npos) |
7e1bcce3 | 300 | { |
2e721daa AP |
301 | unsigned cimp = parse_field (buf); |
302 | if (cimp == INVALID_IMP) | |
303 | goto not_found; | |
304 | ||
305 | if (imp == INVALID_IMP) | |
306 | imp = cimp; | |
307 | /* FIXME: BIG.little implementers are always equal. */ | |
308 | else if (imp != cimp) | |
309 | goto not_found; | |
7e1bcce3 | 310 | } |
b399f3c6 | 311 | else if (find_field (buf, "variant") != std::string::npos) |
e8fcc9fa AP |
312 | { |
313 | unsigned cvariant = parse_field (buf); | |
314 | if (!contains_core_p (variants, cvariant)) | |
315 | { | |
316 | if (n_variants == 2) | |
317 | goto not_found; | |
318 | ||
319 | variants[n_variants++] = cvariant; | |
320 | } | |
321 | continue; | |
322 | } | |
b399f3c6 | 323 | else if (find_field (buf, "part") != std::string::npos) |
7e1bcce3 | 324 | { |
2e721daa | 325 | unsigned ccore = parse_field (buf); |
bd5849af AP |
326 | if (!contains_core_p (cores, ccore)) |
327 | { | |
328 | if (n_cores == 2) | |
329 | goto not_found; | |
330 | ||
331 | cores[n_cores++] = ccore; | |
332 | } | |
8433cb91 JG |
333 | continue; |
334 | } | |
b399f3c6 TC |
335 | else if (!tune && !processed_exts |
336 | && (sep_pos = find_field (buf, "Features")) != std::string::npos) | |
8433cb91 | 337 | { |
b399f3c6 TC |
338 | /* First create the list of features in the buffer. */ |
339 | std::set<std::string> features; | |
340 | /* Drop everything till the :. */ | |
341 | buf = buf.substr (sep_pos + 1); | |
342 | split_words (buf, features); | |
343 | ||
8433cb91 JG |
344 | for (i = 0; i < num_exts; i++) |
345 | { | |
b399f3c6 | 346 | const std::string val (aarch64_extensions[i].feat_string); |
29c6debc TC |
347 | |
348 | /* If the feature contains no HWCAPS string then ignore it for the | |
349 | auto detection. */ | |
b399f3c6 | 350 | if (val.empty ()) |
29c6debc TC |
351 | continue; |
352 | ||
8433cb91 JG |
353 | bool enabled = true; |
354 | ||
355 | /* This may be a multi-token feature string. We need | |
29c6debc | 356 | to match all parts, which could be in any order. */ |
b399f3c6 TC |
357 | std::set<std::string> tokens; |
358 | split_words (val, tokens); | |
359 | std::set<std::string>::iterator it; | |
360 | ||
361 | /* Iterate till the first feature isn't found or all of them | |
362 | are found. */ | |
363 | for (it = tokens.begin (); enabled && it != tokens.end (); ++it) | |
364 | enabled = enabled && features.count (*it); | |
8433cb91 JG |
365 | |
366 | if (enabled) | |
367 | extension_flags |= aarch64_extensions[i].flag; | |
368 | else | |
369 | extension_flags &= ~(aarch64_extensions[i].flag); | |
370 | } | |
371 | ||
372 | processed_exts = true; | |
373 | } | |
7e1bcce3 KT |
374 | } |
375 | ||
376 | fclose (f); | |
377 | f = NULL; | |
378 | ||
379 | /* Weird cpuinfo format that we don't know how to handle. */ | |
e8fcc9fa AP |
380 | if (n_cores == 0 |
381 | || n_cores > 2 | |
382 | || (n_cores == 1 && n_variants != 1) | |
383 | || imp == INVALID_IMP) | |
7e1bcce3 KT |
384 | goto not_found; |
385 | ||
e8fcc9fa AP |
386 | /* Simple case, one core type or just looking for the arch. */ |
387 | if (n_cores == 1 || arch) | |
7e1bcce3 | 388 | { |
bd5849af AP |
389 | /* Search for one of the cores in the list. */ |
390 | for (i = 0; aarch64_cpu_data[i].name != NULL; i++) | |
391 | if (aarch64_cpu_data[i].implementer_id == imp | |
e8fcc9fa AP |
392 | && cores[0] == aarch64_cpu_data[i].part_no |
393 | && (aarch64_cpu_data[i].variant == ALL_VARIANTS | |
394 | || variants[0] == aarch64_cpu_data[i].variant)) | |
395 | break; | |
e91a17fe | 396 | |
e8fcc9fa | 397 | if (aarch64_cpu_data[i].name == NULL) |
e91a17fe | 398 | { |
60dee638 | 399 | auto arch_info = get_arch_from_id (DEFAULT_ARCH); |
e91a17fe TC |
400 | |
401 | gcc_assert (arch_info); | |
e8fcc9fa | 402 | |
e91a17fe TC |
403 | res = concat ("-march=", arch_info->name, NULL); |
404 | default_flags = arch_info->flags; | |
405 | } | |
406 | else if (arch) | |
e8fcc9fa AP |
407 | { |
408 | const char *arch_id = aarch64_cpu_data[i].arch; | |
60dee638 | 409 | auto arch_info = get_arch_from_id (arch_id); |
7e1bcce3 | 410 | |
e8fcc9fa AP |
411 | /* We got some arch indentifier that's not in aarch64-arches.def? */ |
412 | if (!arch_info) | |
413 | goto not_found; | |
7e1bcce3 | 414 | |
e8fcc9fa AP |
415 | res = concat ("-march=", arch_info->name, NULL); |
416 | default_flags = arch_info->flags; | |
417 | } | |
418 | else | |
419 | { | |
420 | default_flags = aarch64_cpu_data[i].flags; | |
421 | res = concat ("-m", | |
422 | cpu ? "cpu" : "tune", "=", | |
423 | aarch64_cpu_data[i].name, | |
424 | NULL); | |
425 | } | |
7e1bcce3 KT |
426 | } |
427 | /* We have big.LITTLE. */ | |
e8fcc9fa | 428 | else |
7e1bcce3 | 429 | { |
8433cb91 JG |
430 | for (i = 0; aarch64_cpu_data[i].name != NULL; i++) |
431 | { | |
2e721daa AP |
432 | if (aarch64_cpu_data[i].implementer_id == imp |
433 | && valid_bL_core_p (cores, aarch64_cpu_data[i].part_no)) | |
8433cb91 JG |
434 | { |
435 | res = concat ("-m", | |
436 | cpu ? "cpu" : "tune", "=", | |
437 | aarch64_cpu_data[i].name, | |
438 | NULL); | |
439 | default_flags = aarch64_cpu_data[i].flags; | |
440 | break; | |
441 | } | |
442 | } | |
7e1bcce3 | 443 | if (!res) |
8433cb91 | 444 | goto not_found; |
7e1bcce3 | 445 | } |
7e1bcce3 KT |
446 | |
447 | if (tune) | |
448 | return res; | |
449 | ||
6770fa53 DM |
450 | { |
451 | std::string extension | |
452 | = aarch64_get_extension_string_for_isa_flags (extension_flags, | |
453 | default_flags); | |
454 | res = concat (res, extension.c_str (), NULL); | |
455 | } | |
7e1bcce3 KT |
456 | |
457 | return res; | |
458 | ||
459 | not_found: | |
460 | { | |
461 | /* If detection fails we ignore the option. | |
29c6debc | 462 | Clean up and return NULL. */ |
7e1bcce3 KT |
463 | |
464 | if (f) | |
465 | fclose (f); | |
466 | ||
29c6debc | 467 | return NULL; |
7e1bcce3 KT |
468 | } |
469 | } | |
470 |