Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (features_cpu.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | ||
26 | #if defined(_WIN32) | |
27 | #include <direct.h> | |
28 | #else | |
29 | #include <unistd.h> | |
30 | #endif | |
31 | ||
32 | #include <compat/strl.h> | |
33 | #include <streams/file_stream.h> | |
34 | #include <libretro.h> | |
35 | #include <features/features_cpu.h> | |
36 | #include <retro_timers.h> | |
37 | ||
38 | #if defined(_WIN32) && !defined(_XBOX) | |
39 | #include <windows.h> | |
40 | #endif | |
41 | ||
42 | #ifdef __PSL1GHT__ | |
43 | #include <lv2/systime.h> | |
44 | #endif | |
45 | ||
46 | #if defined(_XBOX360) | |
47 | #include <PPCIntrinsics.h> | |
48 | #elif !defined(__MACH__) && (defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC64__) || defined(__powerpc64__)) | |
49 | #ifndef _PPU_INTRINSICS_H | |
50 | #include <ppu_intrinsics.h> | |
51 | #endif | |
52 | #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP) | |
53 | /* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */ | |
54 | #include <time.h> | |
55 | #endif | |
56 | ||
57 | #if defined(__QNX__) && !defined(CLOCK_MONOTONIC) | |
58 | #define CLOCK_MONOTONIC 2 | |
59 | #endif | |
60 | ||
61 | #if defined(PSP) | |
62 | #include <pspkernel.h> | |
63 | #endif | |
64 | ||
65 | #if defined(PSP) || defined(__PSL1GHT__) | |
66 | #include <sys/time.h> | |
67 | #endif | |
68 | ||
69 | #if defined(PSP) | |
70 | #include <psprtc.h> | |
71 | #endif | |
72 | ||
73 | #if defined(VITA) | |
74 | #include <psp2/kernel/processmgr.h> | |
75 | #include <psp2/rtc.h> | |
76 | #endif | |
77 | ||
78 | #if defined(ORBIS) | |
79 | #include <orbis/libkernel.h> | |
80 | #endif | |
81 | ||
82 | #if defined(PS2) | |
83 | #include <ps2sdkapi.h> | |
84 | #endif | |
85 | ||
86 | #if !defined(__PSL1GHT__) && defined(__PS3__) | |
87 | #include <sys/sys_time.h> | |
88 | #endif | |
89 | ||
90 | #ifdef GEKKO | |
91 | #include <ogc/lwp_watchdog.h> | |
92 | #endif | |
93 | ||
94 | #ifdef WIIU | |
95 | #include <wiiu/os/time.h> | |
96 | #endif | |
97 | ||
98 | #if defined(HAVE_LIBNX) | |
99 | #include <switch.h> | |
100 | #elif defined(SWITCH) | |
101 | #include <libtransistor/types.h> | |
102 | #include <libtransistor/svc.h> | |
103 | #endif | |
104 | ||
105 | #if defined(_3DS) | |
106 | #include <3ds/svc.h> | |
107 | #include <3ds/os.h> | |
108 | #include <3ds/services/cfgu.h> | |
109 | #endif | |
110 | ||
111 | /* iOS/OSX specific. Lacks clock_gettime(), so implement it. */ | |
112 | #ifdef __MACH__ | |
113 | #include <sys/time.h> | |
114 | ||
115 | #ifndef CLOCK_MONOTONIC | |
116 | #define CLOCK_MONOTONIC 0 | |
117 | #endif | |
118 | ||
119 | #ifndef CLOCK_REALTIME | |
120 | #define CLOCK_REALTIME 0 | |
121 | #endif | |
122 | ||
123 | /** | |
124 | * TODO/FIXME: clock_gettime function is part of iOS 10 now | |
125 | **/ | |
126 | static int ra_clock_gettime(int clk_ik, struct timespec *t) | |
127 | { | |
128 | struct timeval now; | |
129 | int rv = gettimeofday(&now, NULL); | |
130 | if (rv) | |
131 | return rv; | |
132 | t->tv_sec = now.tv_sec; | |
133 | t->tv_nsec = now.tv_usec * 1000; | |
134 | return 0; | |
135 | } | |
136 | #endif | |
137 | ||
138 | #if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000 | |
139 | #else | |
140 | #define ra_clock_gettime clock_gettime | |
141 | #endif | |
142 | ||
143 | #ifdef EMSCRIPTEN | |
144 | #include <emscripten.h> | |
145 | #endif | |
146 | ||
147 | #if defined(BSD) || defined(__APPLE__) | |
148 | #include <sys/sysctl.h> | |
149 | #endif | |
150 | ||
151 | #include <string.h> | |
152 | ||
153 | /** | |
154 | * cpu_features_get_perf_counter: | |
155 | * | |
156 | * Gets performance counter. | |
157 | * | |
158 | * @return Performance counter. | |
159 | **/ | |
160 | retro_perf_tick_t cpu_features_get_perf_counter(void) | |
161 | { | |
162 | retro_perf_tick_t time_ticks = 0; | |
163 | #if defined(_WIN32) | |
164 | long tv_sec, tv_usec; | |
165 | #if defined(_MSC_VER) && _MSC_VER <= 1200 | |
166 | static const unsigned __int64 epoch = 11644473600000000; | |
167 | #else | |
168 | static const unsigned __int64 epoch = 11644473600000000ULL; | |
169 | #endif | |
170 | FILETIME file_time; | |
171 | SYSTEMTIME system_time; | |
172 | ULARGE_INTEGER ularge; | |
173 | ||
174 | GetSystemTime(&system_time); | |
175 | SystemTimeToFileTime(&system_time, &file_time); | |
176 | ularge.LowPart = file_time.dwLowDateTime; | |
177 | ularge.HighPart = file_time.dwHighDateTime; | |
178 | ||
179 | tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L); | |
180 | tv_usec = (long)(system_time.wMilliseconds * 1000); | |
181 | time_ticks = (1000000 * tv_sec + tv_usec); | |
182 | #elif defined(GEKKO) | |
183 | time_ticks = gettime(); | |
184 | #elif !defined(__MACH__) && (defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__PSL1GHT__) || defined(__PPC64__) || defined(__powerpc64__)) | |
185 | time_ticks = __mftb(); | |
186 | #elif (defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0) || defined(__QNX__) || defined(ANDROID) | |
187 | struct timespec tv; | |
188 | if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0) | |
189 | time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 + | |
190 | (retro_perf_tick_t)tv.tv_nsec; | |
191 | ||
192 | #elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(_M_X64) || defined(_M_AMD64) | |
193 | __asm__ volatile ("rdtsc" : "=A" (time_ticks)); | |
194 | #elif defined(__GNUC__) && defined(__x86_64__) || defined(_M_IX86) | |
195 | unsigned a, d; | |
196 | __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); | |
197 | time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32); | |
198 | #elif defined(__ARM_ARCH_6__) | |
199 | __asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) ); | |
200 | #elif defined(__aarch64__) | |
201 | __asm__ volatile( "mrs %0, cntvct_el0" : "=r"(time_ticks) ); | |
202 | #elif defined(PSP) || defined(VITA) | |
203 | time_ticks = sceKernelGetSystemTimeWide(); | |
204 | #elif defined(ORBIS) | |
205 | sceRtcGetCurrentTick((SceRtcTick*)&time_ticks); | |
206 | #elif defined(PS2) | |
207 | time_ticks = ps2_clock(); | |
208 | #elif defined(_3DS) | |
209 | time_ticks = svcGetSystemTick(); | |
210 | #elif defined(WIIU) | |
211 | time_ticks = OSGetSystemTime(); | |
212 | #elif defined(HAVE_LIBNX) | |
213 | time_ticks = armGetSystemTick(); | |
214 | #elif defined(EMSCRIPTEN) | |
215 | time_ticks = emscripten_get_now() * 1000; | |
216 | #endif | |
217 | ||
218 | return time_ticks; | |
219 | } | |
220 | ||
221 | /** | |
222 | * cpu_features_get_time_usec: | |
223 | * | |
224 | * Gets time in microseconds. | |
225 | * | |
226 | * @return Time in microseconds. | |
227 | **/ | |
228 | retro_time_t cpu_features_get_time_usec(void) | |
229 | { | |
230 | #if defined(_WIN32) | |
231 | static LARGE_INTEGER freq; | |
232 | LARGE_INTEGER count; | |
233 | ||
234 | /* Frequency is guaranteed to not change. */ | |
235 | if (!freq.QuadPart && !QueryPerformanceFrequency(&freq)) | |
236 | return 0; | |
237 | ||
238 | if (!QueryPerformanceCounter(&count)) | |
239 | return 0; | |
240 | return (count.QuadPart / freq.QuadPart * 1000000) + (count.QuadPart % freq.QuadPart * 1000000 / freq.QuadPart); | |
241 | #elif defined(__PSL1GHT__) | |
242 | return sysGetSystemTime(); | |
243 | #elif !defined(__PSL1GHT__) && defined(__PS3__) | |
244 | return sys_time_get_system_time(); | |
245 | #elif defined(GEKKO) | |
246 | return ticks_to_microsecs(gettime()); | |
247 | #elif defined(WIIU) | |
248 | return ticks_to_us(OSGetSystemTime()); | |
249 | #elif defined(SWITCH) || defined(HAVE_LIBNX) | |
250 | return (svcGetSystemTick() * 10) / 192; | |
251 | #elif defined(_3DS) | |
252 | return osGetTime() * 1000; | |
253 | #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__) | |
254 | struct timespec tv; | |
255 | if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0) | |
256 | return 0; | |
257 | return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000; | |
258 | #elif defined(EMSCRIPTEN) | |
259 | return emscripten_get_now() * 1000; | |
260 | #elif defined(PS2) | |
261 | return ps2_clock() / PS2_CLOCKS_PER_MSEC * 1000; | |
262 | #elif defined(VITA) || defined(PSP) | |
263 | return sceKernelGetSystemTimeWide(); | |
264 | #elif defined(DJGPP) | |
265 | return uclock() * 1000000LL / UCLOCKS_PER_SEC; | |
266 | #elif defined(ORBIS) | |
267 | return sceKernelGetProcessTime(); | |
268 | #else | |
269 | #error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue." | |
270 | #endif | |
271 | } | |
272 | ||
273 | #if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310) | |
274 | #define CPU_X86 | |
275 | #endif | |
276 | ||
277 | #if defined(_MSC_VER) && !defined(_XBOX) | |
278 | #if (_MSC_VER > 1310) | |
279 | #include <intrin.h> | |
280 | #endif | |
281 | #endif | |
282 | ||
283 | #if defined(CPU_X86) && !defined(__MACH__) | |
284 | void x86_cpuid(int func, int flags[4]) | |
285 | { | |
286 | /* On Android, we compile RetroArch with PIC, and we | |
287 | * are not allowed to clobber the ebx register. */ | |
288 | #ifdef __x86_64__ | |
289 | #define REG_b "rbx" | |
290 | #define REG_S "rsi" | |
291 | #else | |
292 | #define REG_b "ebx" | |
293 | #define REG_S "esi" | |
294 | #endif | |
295 | ||
296 | #if defined(__GNUC__) | |
297 | __asm__ volatile ( | |
298 | "mov %%" REG_b ", %%" REG_S "\n" | |
299 | "cpuid\n" | |
300 | "xchg %%" REG_b ", %%" REG_S "\n" | |
301 | : "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3]) | |
302 | : "a"(func)); | |
303 | #elif defined(_MSC_VER) | |
304 | __cpuid(flags, func); | |
305 | #else | |
306 | #ifndef NDEBUG | |
307 | printf("Unknown compiler. Cannot check CPUID with inline assembly.\n"); | |
308 | #endif | |
309 | memset(flags, 0, 4 * sizeof(int)); | |
310 | #endif | |
311 | } | |
312 | ||
313 | /* Only runs on i686 and above. Needs to be conditionally run. */ | |
314 | static uint64_t xgetbv_x86(uint32_t idx) | |
315 | { | |
316 | #if defined(__GNUC__) | |
317 | uint32_t eax, edx; | |
318 | __asm__ volatile ( | |
319 | /* Older GCC versions (Apple's GCC for example) do | |
320 | * not understand xgetbv instruction. | |
321 | * Stamp out the machine code directly. | |
322 | */ | |
323 | ".byte 0x0f, 0x01, 0xd0\n" | |
324 | : "=a"(eax), "=d"(edx) : "c"(idx)); | |
325 | return ((uint64_t)edx << 32) | eax; | |
326 | #elif _MSC_FULL_VER >= 160040219 | |
327 | /* Intrinsic only works on 2010 SP1 and above. */ | |
328 | return _xgetbv(idx); | |
329 | #else | |
330 | #ifndef NDEBUG | |
331 | printf("Unknown compiler. Cannot check xgetbv bits.\n"); | |
332 | #endif | |
333 | return 0; | |
334 | #endif | |
335 | } | |
336 | #endif | |
337 | ||
338 | #if defined(__ARM_NEON__) | |
339 | #if defined(__arm__) | |
340 | static void arm_enable_runfast_mode(void) | |
341 | { | |
342 | /* RunFast mode. Enables flush-to-zero and some | |
343 | * floating point optimizations. */ | |
344 | static const unsigned x = 0x04086060; | |
345 | static const unsigned y = 0x03000000; | |
346 | int r; | |
347 | __asm__ volatile( | |
348 | "fmrx %0, fpscr \n\t" /* r0 = FPSCR */ | |
349 | "and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */ | |
350 | "orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */ | |
351 | "fmxr fpscr, %0 \n\t" /* FPSCR = r0 */ | |
352 | : "=r"(r) | |
353 | : "r"(x), "r"(y) | |
354 | ); | |
355 | } | |
356 | #endif | |
357 | #endif | |
358 | ||
359 | #if defined(__linux__) && !defined(CPU_X86) | |
360 | static unsigned char check_arm_cpu_feature(const char* feature) | |
361 | { | |
362 | char line[1024]; | |
363 | unsigned char status = 0; | |
364 | RFILE *fp = filestream_open("/proc/cpuinfo", | |
365 | RETRO_VFS_FILE_ACCESS_READ, | |
366 | RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
367 | ||
368 | if (!fp) | |
369 | return 0; | |
370 | ||
371 | while (filestream_gets(fp, line, sizeof(line))) | |
372 | { | |
373 | if (strncmp(line, "Features\t: ", 11)) | |
374 | continue; | |
375 | ||
376 | if (strstr(line + 11, feature)) | |
377 | status = 1; | |
378 | ||
379 | break; | |
380 | } | |
381 | ||
382 | filestream_close(fp); | |
383 | ||
384 | return status; | |
385 | } | |
386 | ||
387 | #if !defined(_SC_NPROCESSORS_ONLN) | |
388 | /** | |
389 | * parse_decimal: | |
390 | * | |
391 | * Parse an decimal integer starting from 'input', but not going further | |
392 | * than 'limit'. Return the value into '*result'. | |
393 | * | |
394 | * NOTE: Does not skip over leading spaces, or deal with sign characters. | |
395 | * NOTE: Ignores overflows. | |
396 | * | |
397 | * The function returns NULL in case of error (bad format), or the new | |
398 | * position after the decimal number in case of success (which will always | |
399 | * be <= 'limit'). | |
400 | * | |
401 | * Leaf function. | |
402 | **/ | |
403 | static const char *parse_decimal(const char* input, | |
404 | const char* limit, int* result) | |
405 | { | |
406 | const char* p = input; | |
407 | int val = 0; | |
408 | ||
409 | while (p < limit) | |
410 | { | |
411 | int d = (*p - '0'); | |
412 | if ((unsigned)d >= 10U) | |
413 | break; | |
414 | val = val*10 + d; | |
415 | p++; | |
416 | } | |
417 | if (p == input) | |
418 | return NULL; | |
419 | ||
420 | *result = val; | |
421 | return p; | |
422 | } | |
423 | ||
424 | /** | |
425 | * cpulist_parse: | |
426 | * Parse a textual list of cpus and store the result inside a CpuList object. | |
427 | * Input format is the following: | |
428 | * - comma-separated list of items (no spaces) | |
429 | * - each item is either a single decimal number (cpu index), or a range made | |
430 | * of two numbers separated by a single dash (-). Ranges are inclusive. | |
431 | * | |
432 | * Examples: 0 | |
433 | * 2,4-127,128-143 | |
434 | * 0-1 | |
435 | **/ | |
436 | static void cpulist_parse(CpuList* list, char **buf, ssize_t length) | |
437 | { | |
438 | const char* p = (const char*)buf; | |
439 | const char* end = p + length; | |
440 | ||
441 | /* NOTE: the input line coming from sysfs typically contains a | |
442 | * trailing newline, so take care of it in the code below | |
443 | */ | |
444 | while (p < end && *p != '\n') | |
445 | { | |
446 | int val, start_value, end_value; | |
447 | /* Find the end of current item, and put it into 'q' */ | |
448 | const char *q = (const char*)memchr(p, ',', end-p); | |
449 | ||
450 | if (!q) | |
451 | q = end; | |
452 | ||
453 | /* Get first value */ | |
454 | if (!(p = parse_decimal(p, q, &start_value))) | |
455 | return; | |
456 | ||
457 | end_value = start_value; | |
458 | ||
459 | /* If we're not at the end of the item, expect a dash and | |
460 | * and integer; extract end value. | |
461 | */ | |
462 | if (p < q && *p == '-') | |
463 | { | |
464 | if (!(p = parse_decimal(p+1, q, &end_value))) | |
465 | return; | |
466 | } | |
467 | ||
468 | /* Set bits CPU list bits */ | |
469 | for (val = start_value; val <= end_value; val++) | |
470 | { | |
471 | if ((unsigned)val < 32) | |
472 | list->mask |= (uint32_t)(UINT32_C(1) << val); | |
473 | } | |
474 | ||
475 | /* Jump to next item */ | |
476 | p = q; | |
477 | if (p < end) | |
478 | p++; | |
479 | } | |
480 | } | |
481 | ||
482 | /** | |
483 | * cpulist_read_from: | |
484 | * | |
485 | * Read a CPU list from one sysfs file | |
486 | **/ | |
487 | static void cpulist_read_from(CpuList* list, const char* filename) | |
488 | { | |
489 | ssize_t length; | |
490 | char *buf = NULL; | |
491 | ||
492 | list->mask = 0; | |
493 | ||
494 | if (filestream_read_file(filename, (void**)&buf, &length) != 1) | |
495 | return; | |
496 | ||
497 | cpulist_parse(list, &buf, length); | |
498 | if (buf) | |
499 | free(buf); | |
500 | buf = NULL; | |
501 | } | |
502 | #endif | |
503 | ||
504 | #endif | |
505 | ||
506 | /** | |
507 | * cpu_features_get_core_amount: | |
508 | * | |
509 | * Gets the amount of available CPU cores. | |
510 | * | |
511 | * @return Amount of CPU cores available. | |
512 | **/ | |
513 | unsigned cpu_features_get_core_amount(void) | |
514 | { | |
515 | #if defined(_WIN32) && !defined(_XBOX) | |
516 | /* Win32 */ | |
517 | SYSTEM_INFO sysinfo; | |
518 | #if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP | |
519 | GetNativeSystemInfo(&sysinfo); | |
520 | #else | |
521 | GetSystemInfo(&sysinfo); | |
522 | #endif | |
523 | return sysinfo.dwNumberOfProcessors; | |
524 | #elif defined(GEKKO) | |
525 | return 1; | |
526 | #elif defined(PSP) || defined(PS2) | |
527 | return 1; | |
528 | #elif defined(__PSL1GHT__) || !defined(__PSL1GHT__) && defined(__PS3__) | |
529 | return 1; /* Only one PPU, SPUs don't really count */ | |
530 | #elif defined(VITA) | |
531 | return 4; | |
532 | #elif defined(HAVE_LIBNX) || defined(SWITCH) | |
533 | return 4; | |
534 | #elif defined(_3DS) | |
535 | u8 device_model = 0xFF; | |
536 | CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ | |
537 | switch (device_model) | |
538 | { | |
539 | case 0: | |
540 | case 1: | |
541 | case 3: | |
542 | /*Old 3/2DS*/ | |
543 | return 2; | |
544 | ||
545 | case 2: | |
546 | case 4: | |
547 | case 5: | |
548 | /*New 3/2DS*/ | |
549 | return 4; | |
550 | ||
551 | default: | |
552 | /*Unknown Device Or Check Failed*/ | |
553 | break; | |
554 | } | |
555 | return 1; | |
556 | #elif defined(WIIU) | |
557 | return 3; | |
558 | #elif defined(_SC_NPROCESSORS_ONLN) | |
559 | /* Linux, most UNIX-likes. */ | |
560 | long ret = sysconf(_SC_NPROCESSORS_ONLN); | |
561 | if (ret <= 0) | |
562 | return (unsigned)1; | |
563 | return (unsigned)ret; | |
564 | #elif defined(BSD) || defined(__APPLE__) | |
565 | /* BSD */ | |
566 | /* Copypasta from stackoverflow, dunno if it works. */ | |
567 | int num_cpu = 0; | |
568 | int mib[4]; | |
569 | size_t len = sizeof(num_cpu); | |
570 | ||
571 | mib[0] = CTL_HW; | |
572 | mib[1] = HW_AVAILCPU; | |
573 | sysctl(mib, 2, &num_cpu, &len, NULL, 0); | |
574 | if (num_cpu < 1) | |
575 | { | |
576 | mib[1] = HW_NCPU; | |
577 | sysctl(mib, 2, &num_cpu, &len, NULL, 0); | |
578 | if (num_cpu < 1) | |
579 | num_cpu = 1; | |
580 | } | |
581 | return num_cpu; | |
582 | #elif defined(__linux__) | |
583 | CpuList cpus_present[1]; | |
584 | CpuList cpus_possible[1]; | |
585 | int amount = 0; | |
586 | ||
587 | cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); | |
588 | cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); | |
589 | ||
590 | /* Compute the intersection of both sets to get the actual number of | |
591 | * CPU cores that can be used on this device by the kernel. | |
592 | */ | |
593 | cpus_present->mask &= cpus_possible->mask; | |
594 | amount = __builtin_popcount(cpus_present->mask); | |
595 | ||
596 | if (amount == 0) | |
597 | return 1; | |
598 | return amount; | |
599 | #elif defined(_XBOX360) | |
600 | return 3; | |
601 | #else | |
602 | /* No idea, assume single core. */ | |
603 | return 1; | |
604 | #endif | |
605 | } | |
606 | ||
607 | /* According to http://en.wikipedia.org/wiki/CPUID */ | |
608 | #define VENDOR_INTEL_b 0x756e6547 | |
609 | #define VENDOR_INTEL_c 0x6c65746e | |
610 | #define VENDOR_INTEL_d 0x49656e69 | |
611 | ||
612 | /** | |
613 | * cpu_features_get: | |
614 | * | |
615 | * Gets CPU features.. | |
616 | * | |
617 | * @return Bitmask of all CPU features available. | |
618 | **/ | |
619 | uint64_t cpu_features_get(void) | |
620 | { | |
621 | uint64_t cpu = 0; | |
622 | #if defined(CPU_X86) && !defined(__MACH__) | |
623 | int vendor_is_intel = 0; | |
624 | const int avx_flags = (1 << 27) | (1 << 28); | |
625 | #endif | |
626 | #if defined(__MACH__) | |
627 | size_t len = sizeof(size_t); | |
628 | ||
629 | if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0) | |
630 | cpu |= RETRO_SIMD_CMOV; | |
631 | ||
632 | #if defined(CPU_X86) | |
633 | len = sizeof(size_t); | |
634 | if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0) | |
635 | cpu |= RETRO_SIMD_MMX | RETRO_SIMD_MMXEXT; | |
636 | ||
637 | len = sizeof(size_t); | |
638 | if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0) | |
639 | cpu |= RETRO_SIMD_SSE; | |
640 | ||
641 | len = sizeof(size_t); | |
642 | if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0) | |
643 | cpu |= RETRO_SIMD_SSE2; | |
644 | ||
645 | len = sizeof(size_t); | |
646 | if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0) | |
647 | cpu |= RETRO_SIMD_SSE3; | |
648 | ||
649 | len = sizeof(size_t); | |
650 | if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0) | |
651 | cpu |= RETRO_SIMD_SSSE3; | |
652 | ||
653 | len = sizeof(size_t); | |
654 | if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0) | |
655 | cpu |= RETRO_SIMD_SSE4; | |
656 | ||
657 | len = sizeof(size_t); | |
658 | if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0) | |
659 | cpu |= RETRO_SIMD_SSE42; | |
660 | ||
661 | len = sizeof(size_t); | |
662 | if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0) | |
663 | cpu |= RETRO_SIMD_AES; | |
664 | ||
665 | len = sizeof(size_t); | |
666 | if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0) | |
667 | cpu |= RETRO_SIMD_AVX; | |
668 | ||
669 | len = sizeof(size_t); | |
670 | if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0) | |
671 | cpu |= RETRO_SIMD_AVX2; | |
672 | ||
673 | len = sizeof(size_t); | |
674 | if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0) | |
675 | cpu |= RETRO_SIMD_VMX; | |
676 | ||
677 | #else | |
678 | len = sizeof(size_t); | |
679 | if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0) | |
680 | cpu |= RETRO_SIMD_NEON; | |
681 | ||
682 | len = sizeof(size_t); | |
683 | if (sysctlbyname("hw.optional.neon_fp16", NULL, &len, NULL, 0) == 0) | |
684 | cpu |= RETRO_SIMD_VFPV3; | |
685 | ||
686 | len = sizeof(size_t); | |
687 | if (sysctlbyname("hw.optional.neon_hpfp", NULL, &len, NULL, 0) == 0) | |
688 | cpu |= RETRO_SIMD_VFPV4; | |
689 | #endif | |
690 | #elif defined(_XBOX1) | |
691 | cpu |= RETRO_SIMD_MMX | RETRO_SIMD_SSE | RETRO_SIMD_MMXEXT; | |
692 | #elif defined(CPU_X86) | |
693 | unsigned max_flag = 0; | |
694 | int flags[4]; | |
695 | int vendor_shuffle[3]; | |
696 | char vendor[13]; | |
697 | x86_cpuid(0, flags); | |
698 | vendor_shuffle[0] = flags[1]; | |
699 | vendor_shuffle[1] = flags[3]; | |
700 | vendor_shuffle[2] = flags[2]; | |
701 | ||
702 | vendor[0] = '\0'; | |
703 | memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle)); | |
704 | ||
705 | /* printf("[CPUID]: Vendor: %s\n", vendor); */ | |
706 | ||
707 | vendor_is_intel = ( | |
708 | flags[1] == VENDOR_INTEL_b && | |
709 | flags[2] == VENDOR_INTEL_c && | |
710 | flags[3] == VENDOR_INTEL_d); | |
711 | ||
712 | max_flag = flags[0]; | |
713 | if (max_flag < 1) /* Does CPUID not support func = 1? (unlikely ...) */ | |
714 | return 0; | |
715 | ||
716 | x86_cpuid(1, flags); | |
717 | ||
718 | if (flags[3] & (1 << 15)) | |
719 | cpu |= RETRO_SIMD_CMOV; | |
720 | ||
721 | if (flags[3] & (1 << 23)) | |
722 | cpu |= RETRO_SIMD_MMX; | |
723 | ||
724 | /* SSE also implies MMXEXT (according to FFmpeg source). */ | |
725 | if (flags[3] & (1 << 25)) | |
726 | cpu |= RETRO_SIMD_SSE | RETRO_SIMD_MMXEXT; | |
727 | ||
728 | if (flags[3] & (1 << 26)) | |
729 | cpu |= RETRO_SIMD_SSE2; | |
730 | ||
731 | if (flags[2] & (1 << 0)) | |
732 | cpu |= RETRO_SIMD_SSE3; | |
733 | ||
734 | if (flags[2] & (1 << 9)) | |
735 | cpu |= RETRO_SIMD_SSSE3; | |
736 | ||
737 | if (flags[2] & (1 << 19)) | |
738 | cpu |= RETRO_SIMD_SSE4; | |
739 | ||
740 | if (flags[2] & (1 << 20)) | |
741 | cpu |= RETRO_SIMD_SSE42; | |
742 | ||
743 | if ((flags[2] & (1 << 23))) | |
744 | cpu |= RETRO_SIMD_POPCNT; | |
745 | ||
746 | if (vendor_is_intel && (flags[2] & (1 << 22))) | |
747 | cpu |= RETRO_SIMD_MOVBE; | |
748 | ||
749 | if (flags[2] & (1 << 25)) | |
750 | cpu |= RETRO_SIMD_AES; | |
751 | ||
752 | /* Must only perform xgetbv check if we have | |
753 | * AVX CPU support (guaranteed to have at least i686). */ | |
754 | if (((flags[2] & avx_flags) == avx_flags) | |
755 | && ((xgetbv_x86(0) & 0x6) == 0x6)) | |
756 | cpu |= RETRO_SIMD_AVX; | |
757 | ||
758 | if (max_flag >= 7) | |
759 | { | |
760 | x86_cpuid(7, flags); | |
761 | if (flags[1] & (1 << 5)) | |
762 | cpu |= RETRO_SIMD_AVX2; | |
763 | } | |
764 | ||
765 | x86_cpuid(0x80000000, flags); | |
766 | max_flag = flags[0]; | |
767 | if (max_flag >= 0x80000001u) | |
768 | { | |
769 | x86_cpuid(0x80000001, flags); | |
770 | if (flags[3] & (1 << 23)) | |
771 | cpu |= RETRO_SIMD_MMX; | |
772 | if (flags[3] & (1 << 22)) | |
773 | cpu |= RETRO_SIMD_MMXEXT; | |
774 | } | |
775 | #elif defined(__linux__) | |
776 | if (check_arm_cpu_feature("neon")) | |
777 | { | |
778 | cpu |= RETRO_SIMD_NEON; | |
779 | #if defined(__ARM_NEON__) && defined(__arm__) | |
780 | arm_enable_runfast_mode(); | |
781 | #endif | |
782 | } | |
783 | ||
784 | if (check_arm_cpu_feature("vfpv3")) | |
785 | cpu |= RETRO_SIMD_VFPV3; | |
786 | ||
787 | if (check_arm_cpu_feature("vfpv4")) | |
788 | cpu |= RETRO_SIMD_VFPV4; | |
789 | ||
790 | if (check_arm_cpu_feature("asimd")) | |
791 | { | |
792 | cpu |= RETRO_SIMD_ASIMD; | |
793 | #ifdef __ARM_NEON__ | |
794 | cpu |= RETRO_SIMD_NEON; | |
795 | #if defined(__arm__) | |
796 | arm_enable_runfast_mode(); | |
797 | #endif | |
798 | #endif | |
799 | } | |
800 | ||
801 | #if 0 | |
802 | check_arm_cpu_feature("swp"); | |
803 | check_arm_cpu_feature("half"); | |
804 | check_arm_cpu_feature("thumb"); | |
805 | check_arm_cpu_feature("fastmult"); | |
806 | check_arm_cpu_feature("vfp"); | |
807 | check_arm_cpu_feature("edsp"); | |
808 | check_arm_cpu_feature("thumbee"); | |
809 | check_arm_cpu_feature("tls"); | |
810 | check_arm_cpu_feature("idiva"); | |
811 | check_arm_cpu_feature("idivt"); | |
812 | #endif | |
813 | ||
814 | #elif defined(__ARM_NEON__) | |
815 | cpu |= RETRO_SIMD_NEON; | |
816 | #if defined(__arm__) | |
817 | arm_enable_runfast_mode(); | |
818 | #endif | |
819 | #elif defined(__ALTIVEC__) | |
820 | cpu |= RETRO_SIMD_VMX; | |
821 | #elif defined(XBOX360) | |
822 | cpu |= RETRO_SIMD_VMX128; | |
823 | #elif defined(PSP) || defined(PS2) | |
824 | cpu |= RETRO_SIMD_VFPU; | |
825 | #elif defined(GEKKO) | |
826 | cpu |= RETRO_SIMD_PS; | |
827 | #endif | |
828 | ||
829 | return cpu; | |
830 | } | |
831 | ||
832 | void cpu_features_get_model_name(char *name, int len) | |
833 | { | |
834 | #if defined(CPU_X86) && !defined(__MACH__) | |
835 | union { | |
836 | int32_t i[4]; | |
837 | uint32_t u[4]; | |
838 | uint8_t s[16]; | |
839 | } flags; | |
840 | int i, j; | |
841 | int pos = 0; | |
842 | bool start = false; | |
843 | ||
844 | if (!name) | |
845 | return; | |
846 | ||
847 | x86_cpuid(0x80000000, flags.i); | |
848 | ||
849 | /* Check for additional cpuid attributes availability */ | |
850 | if (flags.u[0] < 0x80000004) | |
851 | return; | |
852 | ||
853 | for (i = 0; i < 3; i++) | |
854 | { | |
855 | memset(flags.i, 0, sizeof(flags.i)); | |
856 | x86_cpuid(0x80000002 + i, flags.i); | |
857 | ||
858 | for (j = 0; j < (int)sizeof(flags.s); j++) | |
859 | { | |
860 | if (!start && flags.s[j] == ' ') | |
861 | continue; | |
862 | else | |
863 | start = true; | |
864 | ||
865 | if (pos == len - 1) | |
866 | { | |
867 | /* truncate if we ran out of room */ | |
868 | name[pos] = '\0'; | |
869 | goto end; | |
870 | } | |
871 | ||
872 | name[pos++] = flags.s[j]; | |
873 | } | |
874 | } | |
875 | end: | |
876 | /* terminate our string */ | |
877 | if (pos < len) | |
878 | name[pos] = '\0'; | |
879 | #elif defined(__MACH__) | |
880 | if (!name) | |
881 | return; | |
882 | { | |
883 | size_t len_size = len; | |
884 | sysctlbyname("machdep.cpu.brand_string", name, &len_size, NULL, 0); | |
885 | } | |
886 | #elif defined(__linux__) | |
887 | if (!name) | |
888 | return; | |
889 | { | |
890 | char *model_name, line[128]; | |
891 | RFILE *fp = filestream_open("/proc/cpuinfo", | |
892 | RETRO_VFS_FILE_ACCESS_READ, | |
893 | RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
894 | ||
895 | if (!fp) | |
896 | return; | |
897 | ||
898 | while (filestream_gets(fp, line, sizeof(line))) | |
899 | { | |
900 | if (strncmp(line, "model name", 10)) | |
901 | continue; | |
902 | ||
903 | if ((model_name = strstr(line + 10, ": "))) | |
904 | { | |
905 | model_name += 2; | |
906 | strncpy(name, model_name, len); | |
907 | name[len - 1] = '\0'; | |
908 | } | |
909 | ||
910 | break; | |
911 | } | |
912 | ||
913 | filestream_close(fp); | |
914 | } | |
915 | #else | |
916 | if (!name) | |
917 | return; | |
918 | return; | |
919 | #endif | |
920 | } |