git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / libretro-common / features / features_cpu.c
CommitLineData
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 **/
126static 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 **/
160retro_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 **/
228retro_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__)
284void 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. */
314static 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__)
340static 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)
360static 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 **/
403static 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 **/
436static 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 **/
487static 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 **/
513unsigned 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 **/
619uint64_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
832void 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 }
875end:
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}