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