/* Copyright (C) 2010-2020 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (features_cpu.c). * --------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, * to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #if defined(_WIN32) #include #else #include #endif #include #include #include #include "cpu_class.h" /* per-processor class, shared with rthreads */ #if defined(__linux__) #include /* The prototype every Linux libc uses; spelled out so a strict C89 * build, where glibc hides it, sees the same one. */ extern long syscall(long number, ...); #endif #include #include #if defined(_WIN32) && !defined(_XBOX) #include #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) #include #endif #endif #ifdef __PSL1GHT__ #include #endif #if defined(_XBOX360) #include #elif !defined(__MACH__) && !defined(__FreeBSD__) && (defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC64__) || defined(__powerpc64__)) #ifndef _PPU_INTRINSICS_H #include #endif #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP) /* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */ #include #endif #if defined(__QNX__) && !defined(CLOCK_MONOTONIC) #define CLOCK_MONOTONIC 2 #endif #if defined(PSP) #include #endif #if defined(PSP) || defined(__PSL1GHT__) #include #endif #if defined(PSP) #include #endif #if defined(VITA) #include #include #endif #if defined(ORBIS) #include #endif #if defined(PS2) #include #endif #if !defined(__PSL1GHT__) && defined(__PS3__) #include #endif #if defined(GEKKO_NATIVE) #include #elif defined(GEKKO) #include #endif #ifdef WIIU #include #endif #if defined(HAVE_LIBNX) #include #elif defined(SWITCH) #include #include #endif #if defined(_3DS) #include <3ds/svc.h> #include <3ds/os.h> #include <3ds/services/cfgu.h> #endif #if defined(WEBOS) #include #endif /* iOS/OSX specific. Lacks clock_gettime(), so implement it. */ #ifdef __MACH__ #include #include #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 0 #endif #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 0 #endif /* clock_gettime() was added in iOS 10.0 / macOS 10.12 Sierra. * On deployment targets below those versions the symbol doesn't * exist in libSystem and the link fails, so fall back to a * gettimeofday() shim. Gated on MIN_REQUIRED (deployment target) * rather than MAX_ALLOWED (SDK) because the binary needs to run on * the deployment target, and Apple weak-links clock_gettime when * targeting < 10.12 even from a newer SDK. */ #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) \ && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000) \ || (defined(MAC_OS_X_VERSION_MIN_REQUIRED) \ && MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #define RA_NEEDS_CLOCK_GETTIME_SHIM 1 static int ra_clock_gettime(int clk_ik, struct timespec *t) { struct timeval now; int rv = gettimeofday(&now, NULL); if (rv) return rv; t->tv_sec = now.tv_sec; t->tv_nsec = now.tv_usec * 1000; return 0; } #endif #endif #if defined(__MACH__) && defined(RA_NEEDS_CLOCK_GETTIME_SHIM) #else #define ra_clock_gettime clock_gettime #endif #ifdef __EMSCRIPTEN__ #include #endif #if defined(BSD) || defined(__APPLE__) #include #endif #include retro_perf_tick_t cpu_features_get_perf_counter(void) { retro_perf_tick_t time_ticks = 0; /* The CPU's own cycle counter wherever there is one: a register * read, no clock behind it. Platform clocks follow for the rest. */ #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) time_ticks = (retro_perf_tick_t)__rdtsc(); #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) { unsigned a, d; __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32); } #elif defined(__GNUC__) && defined(__aarch64__) __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r" (time_ticks)); #elif defined(__GNUC__) && defined(__ARM_ARCH_6__) __asm__ __volatile__ ("mrc p15, 0, %0, c9, c13, 0" : "=r" (time_ticks)); #elif defined(_WIN32) { LARGE_INTEGER c; QueryPerformanceCounter(&c); time_ticks = (retro_perf_tick_t)c.QuadPart; } #elif defined(GEKKO_NATIVE) time_ticks = gk_ticks(); #elif defined(GEKKO) time_ticks = gettime(); #elif !defined(__MACH__) && !defined(__FreeBSD__) && (defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__) || defined(__PSL1GHT__) || defined(__PPC64__) || defined(__powerpc64__)) time_ticks = __mftb(); #elif (defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0) || defined(__QNX__) || defined(ANDROID) { struct timespec tv; if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0) time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 + (retro_perf_tick_t)tv.tv_nsec; } #elif defined(PSP) || defined(VITA) time_ticks = sceKernelGetSystemTimeWide(); #elif defined(ORBIS) sceRtcGetCurrentTick((SceRtcTick*)&time_ticks); #elif defined(PS2) time_ticks = ps2_clock(); #elif defined(_3DS) time_ticks = svcGetSystemTick(); #elif defined(WIIU) time_ticks = OSGetSystemTime(); #elif defined(HAVE_LIBNX) time_ticks = armGetSystemTick(); #elif defined(__EMSCRIPTEN__) time_ticks = emscripten_get_now() * 1000; #endif return time_ticks; } retro_time_t cpu_features_get_time_usec(void) { #if defined(_WIN32) static LARGE_INTEGER freq; LARGE_INTEGER count; /* Frequency is guaranteed to not change. */ if (!freq.QuadPart && !QueryPerformanceFrequency(&freq)) return 0; if (!QueryPerformanceCounter(&count)) return 0; return (count.QuadPart / freq.QuadPart * 1000000) + (count.QuadPart % freq.QuadPart * 1000000 / freq.QuadPart); #elif defined(__PSL1GHT__) return sysGetSystemTime(); #elif !defined(__PSL1GHT__) && defined(__PS3__) return sys_time_get_system_time(); #elif defined(GEKKO_NATIVE) return gk_ticks_to_us(gk_ticks()); #elif defined(GEKKO) return ticks_to_microsecs(gettime()); #elif defined(WIIU) return ticks_to_us(OSGetSystemTime()); #elif defined(SWITCH) || defined(HAVE_LIBNX) return (svcGetSystemTick() * 10) / 192; #elif defined(_3DS) return osGetTime() * 1000; #elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__) struct timespec tv; tv.tv_sec = 0; tv.tv_nsec = 0; if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0) return 0; return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000; #elif defined(__EMSCRIPTEN__) return emscripten_get_now() * 1000; #elif defined(PS2) return ps2_clock() / PS2_CLOCKS_PER_MSEC * 1000; #elif defined(VITA) || defined(PSP) return sceKernelGetSystemTimeWide(); #elif defined(DJGPP) return uclock() * 1000000LL / UCLOCKS_PER_SEC; #elif defined(ORBIS) return sceKernelGetProcessTime(); #else #error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue." #endif } #if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310) #define CPU_X86 #endif #if defined(_MSC_VER) && !defined(_XBOX) #if (_MSC_VER > 1310) #include #endif #endif #if defined(CPU_X86) && !defined(__MACH__) #include void x86_cpuid(uint32_t func, int32_t flags[4]) { /* On Android, we compile RetroArch with PIC, and we * are not allowed to clobber the ebx register. */ #ifdef __x86_64__ #define REG_b "rbx" #define REG_S "rsi" #else #define REG_b "ebx" #define REG_S "esi" #endif #if defined(__GNUC__) __asm__ volatile ( "mov %%" REG_b ", %%" REG_S "\n" "cpuid\n" "xchg %%" REG_b ", %%" REG_S "\n" : "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3]) : "a"(func)); #elif defined(_MSC_VER) && INT_MAX == 2147483647 __cpuid((int*)flags, func); #else #ifndef NDEBUG printf("Unknown compiler. Cannot check CPUID with inline assembly.\n"); #endif memset(flags, 0, 4 * sizeof(int)); #endif } /* Only runs on i686 and above. Needs to be conditionally run. */ static uint64_t xgetbv_x86(uint32_t idx) { #if defined(__GNUC__) uint32_t eax, edx; __asm__ volatile ( /* Older GCC versions (Apple's GCC for example) do * not understand xgetbv instruction. * Stamp out the machine code directly. */ ".byte 0x0f, 0x01, 0xd0\n" : "=a"(eax), "=d"(edx) : "c"(idx)); return ((uint64_t)edx << 32) | eax; #elif _MSC_FULL_VER >= 160040219 /* Intrinsic only works on 2010 SP1 and above. */ return _xgetbv(idx); #else #ifndef NDEBUG printf("Unknown compiler. Cannot check xgetbv bits.\n"); #endif return 0; #endif } #endif /* RunFast mode is a 32-bit VFP control, and this writes FPSCR - so it * needs both an ARM32 target and an FPU worth configuring. NEON * implies VFP, which is why its presence is the proxy used here; a * VFP-less core such as the armv5te arm926ej-s would fault on the * write. * * CPU_ARM_RUNFAST is the single condition for the definition AND * every call. They were separate conditions and drifted: the calls * ended up reachable where the definition was not, which is a link * error on exactly the targets with no NEON - Miyoo armv5te and * armv7 builds without an FPU selected. */ #if (defined(__ARM_NEON) || defined(__ARM_NEON__)) && defined(__arm__) #define CPU_ARM_RUNFAST 1 #endif #ifdef CPU_ARM_RUNFAST static void arm_enable_runfast_mode(void) { /* RunFast mode. Enables flush-to-zero and some * floating point optimizations. */ static const unsigned x = 0x04086060; static const unsigned y = 0x03000000; int r; __asm__ volatile( "fmrx %0, fpscr \n\t" /* r0 = FPSCR */ "and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */ "orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */ "fmxr fpscr, %0 \n\t" /* FPSCR = r0 */ : "=r"(r) : "r"(x), "r"(y) ); } #endif /* CPU_ARM_RUNFAST */ #if defined(__linux__) && !defined(CPU_X86) static unsigned char check_arm_cpu_feature(const char* feature) { char line[1024]; unsigned char status = 0; FILE *fp = fopen("/proc/cpuinfo", "r"); if (!fp) return 0; while (fgets(line, sizeof(line), fp)) { const char *list; const char *p; size_t flen; if (strncmp(line, "Features\t: ", 11)) continue; /* Feature names are space separated and several are prefixes of * others - 'aes' sits inside 'sveaes', 'sha3' inside 'svesha3', * 'asimd' inside 'asimddp' - so compare whole tokens. */ flen = strlen(feature); list = line + 11; for (p = list; (p = strstr(p, feature)); p += flen) { char after = p[flen]; char before = (p == list) ? ' ' : p[-1]; if ( (before == ' ') && (after == ' ' || after == '\n' || after == '\0')) { status = 1; break; } } break; } fclose(fp); return status; } #if !defined(_SC_NPROCESSORS_ONLN) /** * parse_decimal: * * Parse an decimal integer starting from 'input', but not going further * than 'limit'. Return the value into '*result'. * * NOTE: Does not skip over leading spaces, or deal with sign characters. * NOTE: Ignores overflows. * * The function returns NULL in case of error (bad format), or the new * position after the decimal number in case of success (which will always * be <= 'limit'). * * Leaf function. **/ static const char *parse_decimal(const char* input, const char* limit, int* result) { const char* p = input; int val = 0; while (p < limit) { int d = (*p - '0'); if ((unsigned)d >= 10U) break; val = val*10 + d; p++; } if (p == input) return NULL; *result = val; return p; } /** * cpulist_parse: * Parse a textual list of cpus and store the result inside a CpuList object. * Input format is the following: * - comma-separated list of items (no spaces) * - each item is either a single decimal number (cpu index), or a range made * of two numbers separated by a single dash (-). Ranges are inclusive. * * Examples: 0 * 2,4-127,128-143 * 0-1 **/ static void cpulist_parse(CpuList* list, const char *buf, ssize_t len) { const char* p = buf; const char* end = p + len; /* NOTE: the input line coming from sysfs typically contains a * trailing newline, so take care of it in the code below */ while (p < end && *p != '\n') { int val, start_value, end_value; /* Find the end of current item, and put it into 'q' */ const char *q = (const char*)memchr(p, ',', end-p); if (!q) q = end; /* Get first value */ if (!(p = parse_decimal(p, q, &start_value))) return; end_value = start_value; /* If we're not at the end of the item, expect a dash and * and integer; extract end value. */ if (p < q && *p == '-') { if (!(p = parse_decimal(p+1, q, &end_value))) return; } /* Set bits CPU list bits */ for (val = start_value; val <= end_value; val++) { if ((unsigned)val < 32) list->mask |= (uint32_t)(UINT32_C(1) << val); } /* Jump to next item */ p = q; if (p < end) p++; } } /** * cpulist_read_from: * * Read a CPU list from one sysfs file **/ static void cpulist_read_from(CpuList* list, const char* filename) { char buf[512]; size_t _len; FILE *fp = fopen(filename, "r"); list->mask = 0; if (!fp) return; _len = fread(buf, 1, sizeof(buf) - 1, fp); fclose(fp); buf[_len] = '\0'; cpulist_parse(list, buf, (ssize_t)_len); } #endif #endif #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) /* GetLogicalProcessorInformation arrived in XP SP3 and Server 2003, so * it is resolved rather than linked: on Win9x, NT 4 and 2000 the export * is absent, GetProcAddress reports so, and the caller falls back to * the plain processor count. The Ex form carrying EfficiencyClass is * Windows 7 and later and would rank efficiency cores, which this does * not attempt. */ typedef BOOL (WINAPI *cpu_glpi_t)( PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); /* Caller frees. Returns NULL, leaving *count untouched, wherever the * export or the query is unavailable. */ static SYSTEM_LOGICAL_PROCESSOR_INFORMATION *cpu_win32_slpi(DWORD *count) { SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf = NULL; DWORD _len = 0; HMODULE k32 = GetModuleHandleA("kernel32.dll"); cpu_glpi_t glpi = k32 ? (cpu_glpi_t)(void (*)(void)) GetProcAddress(k32, "GetLogicalProcessorInformation") : NULL; if (!glpi) return NULL; /* The first call is expected to fail, and reports the size wanted. */ if (glpi(NULL, &_len)) return NULL; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || _len == 0) return NULL; if (_len % sizeof(*buf)) return NULL; if (!(buf = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)malloc(_len))) return NULL; if (!glpi(buf, &_len)) { free(buf); return NULL; } *count = _len / (DWORD)sizeof(*buf); return buf; } /* Size in KiB of the level-3 cache whose ProcessorMask covers the * processor, from the same GetLogicalProcessorInformation records * (RelationCache entries carry a level, a size and the mask of * processors behind them); 0 where none does. */ static unsigned cpu_win32_llc_kib( const SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf, DWORD count, unsigned bit) { DWORD i; for (i = 0; i < count; i++) { if (buf[i].Relationship != RelationCache) continue; if (buf[i].Cache.Level != 3) continue; if (buf[i].ProcessorMask & (((ULONG_PTR)1) << bit)) return (unsigned)(buf[i].Cache.Size / 1024); } return 0; } #endif #if defined(__linux__) /* Number of distinct SMT sibling groups under * /sys/devices/system/cpu, which is one per physical core. The list a * sibling file holds names every processor sharing that core, so the * lowest id in it identifies the group and counting distinct ones * counts cores. */ static unsigned linux_core_amount_physical(unsigned logical) { char path[512]; char line[64]; unsigned seen[128]; unsigned n_seen = 0; unsigned found = 0; unsigned i; /* One entry per core, so a machine wider than the table is left to * the logical count rather than answered wrongly. */ if (logical > (unsigned)(sizeof(seen) / sizeof(seen[0]))) return logical; for (i = 0; i < 256 && found < logical; i++) { FILE *fp; unsigned first; unsigned j; snprintf(path, sizeof(path), CPU_CLASS_SYSFS "/cpu%u/topology/thread_siblings_list", i); if (!(fp = fopen(path, "r"))) continue; line[0] = '\0'; if (!fgets(line, sizeof(line), fp)) { fclose(fp); continue; } fclose(fp); found++; if (sscanf(line, "%u", &first) != 1) continue; for (j = 0; j < n_seen; j++) if (seen[j] == first) break; if (j == n_seen && n_seen < (unsigned)(sizeof(seen) / sizeof(seen[0]))) seen[n_seen++] = first; } /* Nothing readable, so the kernel is not publishing topology here. */ if (n_seen == 0) return logical; return n_seen; } #endif #if defined(__linux__) /* First unsigned in a one-line sysfs file, or @fallback where the file * is missing or unreadable. */ static unsigned sysfs_read_uint(const char *path, unsigned fallback) { char line[64]; unsigned val; FILE *fp = fopen(path, "r"); if (!fp) return fallback; line[0] = '\0'; if (!fgets(line, sizeof(line), fp)) { fclose(fp); return fallback; } fclose(fp); if (sscanf(line, "%u", &val) != 1) return fallback; return val; } /* Size of the last-level cache the processor sits behind, in KiB, * from "/cache/index3/size" (a figure with a K or M suffix); 0 * where the kernel publishes none. What separates the two CCDs of an * X3D part, which are one class and near enough one clock. */ static unsigned sysfs_read_llc_kib(unsigned cpu) { char path[512]; char line[64]; unsigned val = 0; char unit = 0; FILE *fp; snprintf(path, sizeof(path), CPU_CLASS_SYSFS "/cpu%u/cache/index3/size", cpu); if (!(fp = fopen(path, "r"))) return 0; line[0] = '\0'; if (!fgets(line, sizeof(line), fp)) line[0] = '\0'; fclose(fp); if (sscanf(line, "%u%c", &val, &unit) < 1) return 0; if (unit == 'M' || unit == 'm') return val * 1024; if (unit == 'G' || unit == 'g') return val * 1024 * 1024; return val; } #endif #if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || defined(__linux__) struct cpu_proc_rank { unsigned cls; /* performance class from cpu_class.h, higher is faster */ unsigned llc; /* last-level cache behind the core, KiB */ unsigned freq; /* kHz, higher is a stronger core within its class */ unsigned id; /* OS processor identifier */ unsigned smt; /* 0 for the first processor on its core, else 1 */ }; /* Fastest class first (the P-cores, the big cluster); within a class * the bigger last-level cache first, then the higher clock; a core * ahead of its own SMT siblings; and the identifier as the tie-break * so the result does not depend on the order the entries were * gathered in. Class comes before everything so an E-core that * happens to clock above a P-core sibling cannot outrank the fast * silicon. Cache comes before clock for the X3D parts: the V-cache * CCD boosts a few percent lower than the other and is the die an * emulator wants to be on. */ static int cpu_proc_rank_cmp(const void *a, const void *b) { const struct cpu_proc_rank *l = (const struct cpu_proc_rank *)a; const struct cpu_proc_rank *r = (const struct cpu_proc_rank *)b; if (l->cls != r->cls) return (l->cls > r->cls) ? -1 : 1; if (l->llc != r->llc) return (l->llc > r->llc) ? -1 : 1; if (l->freq != r->freq) return (l->freq > r->freq) ? -1 : 1; if (l->smt != r->smt) return (l->smt < r->smt) ? -1 : 1; if (l->id != r->id) return (l->id < r->id) ? -1 : 1; return 0; } #endif /* The order, restricted to processors whose bit is set in allowed * (NULL: no restriction). Split from the public function so a test * can rank a fixture topology under a synthetic affinity mask. */ static size_t cpu_features_processor_order_masked( const unsigned char *allowed, unsigned *s, size_t len) { size_t n = 0; #if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || defined(__linux__) unsigned char klass[CPU_CLASS_MAX_IDS]; size_t n_class = cpu_class_read(klass, sizeof(klass)); #define CPU_PROC_CLASS(id) \ (((size_t)(id) < n_class) ? klass[(id)] : 0) #define CPU_PROC_ALLOWED(id) \ (!allowed || ((size_t)(id) < CPU_CLASS_MAX_IDS && allowed[(id)])) #endif if (!s || !len) return 0; #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) { DWORD count = 0; SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf = cpu_win32_slpi(&count); if (buf) { struct cpu_proc_rank *rank; size_t cap = (size_t)cpu_features_get_core_amount(); if (cap < 1) cap = 1; if (cap > 1024) cap = 1024; if ((rank = (struct cpu_proc_rank *) malloc(cap * sizeof(struct cpu_proc_rank)))) { DWORD i; for (i = 0; i < count && n < cap; i++) { unsigned bit; unsigned seen = 0; ULONG_PTR mask = buf[i].ProcessorMask; if (buf[i].Relationship != RelationProcessorCore) continue; /* One mask bit per processor on this core, the lowest * of them being the one an SMT sibling shares with. */ for (bit = 0; bit < sizeof(ULONG_PTR) * 8 && n < cap; bit++) { if (!(mask & (((ULONG_PTR)1) << bit))) continue; if (!CPU_PROC_ALLOWED(bit)) { seen++; continue; } rank[n].cls = CPU_PROC_CLASS(bit); rank[n].llc = cpu_win32_llc_kib(buf, count, bit); rank[n].freq = 0; rank[n].id = bit; rank[n].smt = seen ? 1 : 0; seen++; n++; } } if (n) { qsort(rank, n, sizeof(struct cpu_proc_rank), cpu_proc_rank_cmp); if (n > len) n = len; for (i = 0; i < (DWORD)n; i++) s[i] = rank[i].id; } free(rank); } free(buf); if (n) return n; } } #endif #if defined(__linux__) { struct cpu_proc_rank *rank; char path[512]; unsigned i; /* Every processor is ranked before any is handed back: gathering * only the first @len of them would sort a set chosen by * identifier and hand back the weakest cores on a layout that * numbers the little cluster first. */ size_t cap = 1024; /* every processor the kernel publishes; 16 KiB */ if (!(rank = (struct cpu_proc_rank *) malloc(cap * sizeof(struct cpu_proc_rank)))) return 0; for (i = 0; i < 1024 && n < cap; i++) { unsigned first; snprintf(path, sizeof(path), CPU_CLASS_SYSFS "/cpu%u/topology/thread_siblings_list", i); /* A processor with no sibling list is one the kernel is not * publishing, rather than one that shares no core. */ first = sysfs_read_uint(path, (unsigned)-1); if (first == (unsigned)-1) continue; if (!CPU_PROC_ALLOWED(i)) continue; snprintf(path, sizeof(path), CPU_CLASS_SYSFS "/cpu%u/cpufreq/cpuinfo_max_freq", i); rank[n].cls = CPU_PROC_CLASS(i); rank[n].llc = sysfs_read_llc_kib(i); rank[n].freq = sysfs_read_uint(path, 0); rank[n].id = i; rank[n].smt = (i == first) ? 0 : 1; n++; } if (n) { qsort(rank, n, sizeof(struct cpu_proc_rank), cpu_proc_rank_cmp); if (n > len) n = len; for (i = 0; i < (unsigned)n; i++) s[i] = rank[i].id; } free(rank); if (n) return n; } #endif /* No topology to rank by, so name each processor once in order. */ { unsigned amount = cpu_features_get_core_amount(); unsigned i; for (i = 0; n < len && i < amount; i++) { if (allowed && (i >= CPU_CLASS_MAX_IDS || !allowed[i])) continue; s[n++] = i; } } #if (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) || defined(__linux__) #undef CPU_PROC_CLASS #undef CPU_PROC_ALLOWED #endif return n; } size_t cpu_features_get_processor_order(unsigned *s, size_t len) { #if defined(__linux__) || (defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)) unsigned char allowed[CPU_CLASS_MAX_IDS]; #endif const unsigned char *mask = NULL; #if defined(__linux__) { /* The processors this thread may run on: a pin from a parent * process, a container or the user is a boundary, not something * to hand back as a target. */ unsigned long bits[CPU_CLASS_MAX_IDS / (8 * sizeof(unsigned long))]; memset(bits, 0, sizeof(bits)); if (syscall(__NR_sched_getaffinity, 0, sizeof(bits), bits) > 0) { size_t i; for (i = 0; i < CPU_CLASS_MAX_IDS; i++) allowed[i] = (unsigned char)((bits[i / (8 * sizeof(unsigned long))] >> (i % (8 * sizeof(unsigned long)))) & 1ul); mask = allowed; } } #elif defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) { DWORD_PTR proc = 0, sys = 0; if (GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys) && proc) { size_t i; memset(allowed, 0, sizeof(allowed)); for (i = 0; i < sizeof(DWORD_PTR) * 8; i++) allowed[i] = (unsigned char)((proc >> i) & 1); mask = allowed; } } #endif return cpu_features_processor_order_masked(mask, s, len); } unsigned cpu_features_get_core_amount_physical(void) { unsigned logical = cpu_features_get_core_amount(); #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) { DWORD count = 0; SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf = cpu_win32_slpi(&count); if (buf) { DWORD i; unsigned cores = 0; for (i = 0; i < count; i++) if (buf[i].Relationship == RelationProcessorCore) cores++; free(buf); if (cores > 0) return cores; } } #elif defined(__APPLE__) { /* Darwin publishes both counts, so the physical one is a read * rather than a derivation. */ int val = 0; size_t _len = sizeof(val); if ( sysctlbyname("hw.physicalcpu", &val, &_len, NULL, 0) == 0 && val > 0) return (unsigned)val; } #elif defined(__linux__) return linux_core_amount_physical(logical); #endif /* Every other target either has no SMT to discount or publishes no * way to tell, and the thread count is the safe answer in both * cases. */ return logical; } unsigned cpu_features_get_core_amount(void) { #if defined(_WIN32) && !defined(_XBOX) /* Win32 */ SYSTEM_INFO sysinfo; #if defined(__WINRT__) || (defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) GetNativeSystemInfo(&sysinfo); #else GetSystemInfo(&sysinfo); #endif return sysinfo.dwNumberOfProcessors; #elif defined(GEKKO) return 1; #elif defined(PSP) || defined(PS2) return 1; #elif defined(__PSL1GHT__) || !defined(__PSL1GHT__) && defined(__PS3__) return 1; /* Only one PPU, SPUs don't really count */ #elif defined(VITA) return 4; #elif defined(HAVE_LIBNX) || defined(SWITCH) return 4; #elif defined(_3DS) u8 device_model = 0xFF; CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/ switch (device_model) { case 0: case 1: case 3: /*Old 3/2DS*/ return 2; case 2: case 4: case 5: /*New 3/2DS*/ return 4; default: /*Unknown Device Or Check Failed*/ break; } return 1; #elif defined(WIIU) return 3; #elif defined(_SC_NPROCESSORS_ONLN) /* Linux, most UNIX-likes. */ long ret = sysconf(_SC_NPROCESSORS_ONLN); if (ret <= 0) return (unsigned)1; return (unsigned)ret; #elif defined(BSD) || defined(__APPLE__) /* BSD */ /* Copypasta from stackoverflow, dunno if it works. */ int num_cpu = 0; int mib[4]; size_t _len = sizeof(num_cpu); mib[0] = CTL_HW; mib[1] = HW_AVAILCPU; sysctl(mib, 2, &num_cpu, &_len, NULL, 0); if (num_cpu < 1) { mib[1] = HW_NCPU; sysctl(mib, 2, &num_cpu, &_len, NULL, 0); if (num_cpu < 1) num_cpu = 1; } return num_cpu; #elif defined(__linux__) CpuList cpus_present[1]; CpuList cpus_possible[1]; int amount = 0; cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); /* Compute the intersection of both sets to get the actual number of * CPU cores that can be used on this device by the kernel. */ cpus_present->mask &= cpus_possible->mask; amount = __builtin_popcount(cpus_present->mask); if (amount == 0) return 1; return amount; #elif defined(_XBOX360) return 3; #else /* No idea, assume single core. */ return 1; #endif } /* According to http://en.wikipedia.org/wiki/CPUID */ #define VENDOR_INTEL_b 0x756e6547 #define VENDOR_INTEL_c 0x6c65746e #define VENDOR_INTEL_d 0x49656e69 #if defined(__MACH__) && defined(CPU_X86) /* Whole-token search of one of the machdep.cpu.*features sysctls, each * of which is a space separated list of CPUID feature names. */ static bool darwin_cpu_feature_present(const char *key, const char *want) { char buf[1024]; size_t len = sizeof(buf); size_t wlen = strlen(want); const char *p; buf[0] = '\0'; if (sysctlbyname(key, buf, &len, NULL, 0) != 0) return false; buf[sizeof(buf) - 1] = '\0'; for (p = buf; (p = strstr(p, want)); p += wlen) { char after = p[wlen]; char before = (p == buf) ? ' ' : p[-1]; if ((before == ' ') && (after == ' ' || after == '\0')) return true; } return false; } #endif static uint64_t cpu_features_probe(void) { uint64_t cpu = 0; #if defined(CPU_X86) && !defined(__MACH__) int vendor_is_intel = 0; /* Set once the OS is known to preserve YMM state, which AVX, FMA3 * and FMA4 all depend on. */ int ymm_state = 0; const int avx_flags = (1 << 27) | (1 << 28); #endif #if defined(__MACH__) /* sysctlbyname() returns 0 (success) whenever the key exists, regardless * of its value. On Intel Macs the hw.optional.* keys are always present * but report 0 when the feature is unsupported (e.g. avx512f on pre-Skylake * Xeon CPUs). We therefore have to read the value, not just the success * code. */ int _val = 0; size_t _len = sizeof(_val); if ( sysctlbyname("hw.optional.floatingpoint", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_CMOV; #if defined(CPU_X86) _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.mmx", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_MMX | RETRO_SIMD_MMXEXT; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.sse", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSE; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.sse2", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSE2; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.sse3", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSE3; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.supplementalsse3", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSSE3; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.sse4_1", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSE4; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.sse4_2", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SSE42; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.aes", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_AES; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.avx1_0", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_AVX; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.fma", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_FMA3; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.avx2_0", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_AVX2; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.avx512f", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_AVX512; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.altivec", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_VMX; /* Darwin publishes no hw.optional key for PCLMULQDQ, so read the * CPUID leaf-1 feature-name list instead. Matching is on whole * space-separated tokens: a plain strstr() would also fire on a * hypothetical future "PCLMULQDQ2". */ if (darwin_cpu_feature_present("machdep.cpu.features", "PCLMULQDQ")) cpu |= RETRO_SIMD_PCLMUL; /* SHA-NI lives in the leaf-7 list rather than the leaf-1 one. */ if (darwin_cpu_feature_present("machdep.cpu.leaf7_features", "SHA")) cpu |= RETRO_SIMD_SHA1 | RETRO_SIMD_SHA256; #else _val = 0; _len = sizeof(_val); /* Older key first; newer systems also carry the FEAT_ spelling. * Written out rather than folded into one condition with a comma * operator: the second query needs the buffer and its length reset * first, and doing that inside a short-circuit || is both a warning * and a thing to read twice. */ if (sysctlbyname("hw.optional.armv8_crc32", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_CRC32; else { _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.arm.FEAT_CRC32", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_CRC32; } _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.arm.FEAT_AES", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_AES; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.arm.FEAT_SHA512", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SHA512; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.arm.FEAT_SHA1", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SHA1; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.arm.FEAT_SHA256", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_SHA256; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.neon", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_NEON; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.neon_fp16", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_VFPV3; _val = 0; _len = sizeof(_val); if (sysctlbyname("hw.optional.neon_hpfp", &_val, &_len, NULL, 0) == 0 && _val) cpu |= RETRO_SIMD_VFPV4; #endif #elif defined(_XBOX1) cpu |= RETRO_SIMD_MMX | RETRO_SIMD_SSE | RETRO_SIMD_MMXEXT; #elif defined(CPU_X86) unsigned max_flag = 0; int32_t flags[4]; int vendor_shuffle[3]; char vendor[13]; x86_cpuid(0, flags); vendor_shuffle[0] = flags[1]; vendor_shuffle[1] = flags[3]; vendor_shuffle[2] = flags[2]; vendor[0] = '\0'; memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle)); /* printf("[CPUID]: Vendor: %s\n", vendor); */ vendor_is_intel = ( flags[1] == VENDOR_INTEL_b && flags[2] == VENDOR_INTEL_c && flags[3] == VENDOR_INTEL_d); /* CPUID register contents are unsigned; flags[] is int32_t, so the * widening is spelled out rather than left implicit -- leaf * 0x80000000 legitimately returns values with the top bit set. */ max_flag = (uint32_t)flags[0]; /* Does CPUID not support func = 1? (unlikely ...) */ if (max_flag < 1) return 0; x86_cpuid(1, flags); if (flags[3] & (1 << 15)) cpu |= RETRO_SIMD_CMOV; if (flags[3] & (1 << 23)) cpu |= RETRO_SIMD_MMX; /* SSE also implies MMXEXT (according to FFmpeg source). */ if (flags[3] & (1 << 25)) cpu |= RETRO_SIMD_SSE | RETRO_SIMD_MMXEXT; if (flags[3] & (1 << 26)) cpu |= RETRO_SIMD_SSE2; if (flags[2] & (1 << 0)) cpu |= RETRO_SIMD_SSE3; if (flags[2] & (1 << 9)) cpu |= RETRO_SIMD_SSSE3; if (flags[2] & (1 << 19)) cpu |= RETRO_SIMD_SSE4; if (flags[2] & (1 << 20)) cpu |= RETRO_SIMD_SSE42; if ((flags[2] & (1 << 23))) cpu |= RETRO_SIMD_POPCNT; if (vendor_is_intel && (flags[2] & (1 << 22))) cpu |= RETRO_SIMD_MOVBE; if (flags[2] & (1 << 25)) cpu |= RETRO_SIMD_AES; if (flags[2] & (1 << 1)) cpu |= RETRO_SIMD_PCLMUL; /* Must only perform xgetbv check if we have * AVX CPU support (guaranteed to have at least i686). */ if (((flags[2] & avx_flags) == avx_flags) && ((xgetbv_x86(0) & 0x6) == 0x6)) { ymm_state = 1; cpu |= RETRO_SIMD_AVX; } /* FMA3 accumulates in YMM, so it answers only where the OS keeps * that state. Leaf 1 ECX is still in flags here; the extended leaf * carrying FMA4 is read further down. */ if (ymm_state && (flags[2] & (1 << 12))) cpu |= RETRO_SIMD_FMA3; if (max_flag >= 7) { /* CPUID leaf 7 sub-leaf 0 requires ECX = 0. * x86_cpuid() does not set ECX, so call cpuid directly. */ int32_t flags7[4]; #if defined(__GNUC__) __asm__ volatile ( "mov %%" REG_b ", %%" REG_S "\n" "cpuid\n" "xchg %%" REG_b ", %%" REG_S "\n" : "=a"(flags7[0]), "=S"(flags7[1]), "=c"(flags7[2]), "=d"(flags7[3]) : "a"(7), "c"(0)); #elif defined(_MSC_VER) && INT_MAX == 2147483647 #if _MSC_VER >= 1600 __cpuidex((int*)flags7, 7, 0); #else { int *p = (int*)flags7; __asm { mov eax, 7 xor ecx, ecx cpuid mov esi, p mov [esi], eax mov [esi + 4], ebx mov [esi + 8], ecx mov [esi + 12], edx } } #endif #else memset(flags7, 0, sizeof(flags7)); #endif if (flags7[1] & (1 << 5)) cpu |= RETRO_SIMD_AVX2; /* SHA-NI is one bit covering both digests. */ if (flags7[1] & (1 << 29)) cpu |= RETRO_SIMD_SHA1 | RETRO_SIMD_SHA256; /* AVX-512 Foundation detection. * Requires CPUID leaf 7 sub-leaf 0 EBX bit 16 (AVX-512F), * and OS support for saving ZMM state: * xgetbv XCR0 bits 1,2 (SSE/AVX) and 5,6,7 (opmask, ZMM_Hi256, Hi16_ZMM). */ if ((flags7[1] & (1 << 16)) && ((xgetbv_x86(0) & 0xe6) == 0xe6)) cpu |= RETRO_SIMD_AVX512; /* Leaf 7 EAX carries the highest sub-leaf the CPU implements, so * consult it before asking for sub-leaf 1: an out of range * sub-leaf returns zeroes on current parts but is not documented * to. */ if (flags7[0] >= 1) { int32_t flags71[4]; #if defined(__GNUC__) __asm__ volatile ( "mov %%" REG_b ", %%" REG_S "\n" "cpuid\n" "xchg %%" REG_b ", %%" REG_S "\n" : "=a"(flags71[0]), "=S"(flags71[1]), "=c"(flags71[2]), "=d"(flags71[3]) : "a"(7), "c"(1)); #elif defined(_MSC_VER) && INT_MAX == 2147483647 #if _MSC_VER >= 1600 __cpuidex((int*)flags71, 7, 1); #else { int *p = (int*)flags71; __asm { mov eax, 7 mov ecx, 1 cpuid mov esi, p mov [esi], eax mov [esi + 4], ebx mov [esi + 8], ecx mov [esi + 12], edx } } #endif #else memset(flags71, 0, sizeof(flags71)); #endif if (flags71[0] & (1 << 0)) cpu |= RETRO_SIMD_SHA512; } } x86_cpuid(0x80000000, flags); max_flag = (uint32_t)flags[0]; if (max_flag >= 0x80000001u) { x86_cpuid(0x80000001, flags); if (flags[3] & (1 << 23)) cpu |= RETRO_SIMD_MMX; if (flags[3] & (1 << 22)) cpu |= RETRO_SIMD_MMXEXT; /* LZCNT / ABM (ECX bit 5). macOS x86 uses the sysctl path above and * does not report this bit; consumers there fall back accordingly. */ if (flags[2] & (1 << 5)) cpu |= RETRO_SIMD_LZCNT; /* FMA4, an AMD encoding that no Zen part implements, under the * same OS state as FMA3. */ if (ymm_state && (flags[2] & (1 << 16))) cpu |= RETRO_SIMD_FMA4; } #elif defined(__linux__) if (check_arm_cpu_feature("neon")) { cpu |= RETRO_SIMD_NEON; #ifdef CPU_ARM_RUNFAST arm_enable_runfast_mode(); #endif } if (check_arm_cpu_feature("vfpv3")) cpu |= RETRO_SIMD_VFPV3; if (check_arm_cpu_feature("vfpv4")) cpu |= RETRO_SIMD_VFPV4; /* Part of the optional Cryptographic Extension, which an * implementation may leave out or hold in reset, so a 64-bit ARM * CPU does not imply it. */ if (check_arm_cpu_feature("aes")) cpu |= RETRO_SIMD_AES; /* aarch64 lists it as "crc32" in the Features: line. */ if (check_arm_cpu_feature("crc32")) cpu |= RETRO_SIMD_CRC32; if (check_arm_cpu_feature("sha512")) cpu |= RETRO_SIMD_SHA512; if (check_arm_cpu_feature("sha1")) cpu |= RETRO_SIMD_SHA1; if (check_arm_cpu_feature("sha2")) cpu |= RETRO_SIMD_SHA256; if (check_arm_cpu_feature("asimd")) { cpu |= RETRO_SIMD_ASIMD; /* ASIMD *is* NEON: Advanced SIMD is what aarch64 kernels call * it in the Features: line, where 32-bit ARM says "neon". It * is architecturally mandatory in ARMv8-A, so a CPU reporting * asimd has NEON by definition and callers testing * RETRO_SIMD_NEON must see it. * * This used to be gated on __ARM_NEON__, which is the LEGACY * 32-bit spelling. aarch64 toolchains define __ARM_NEON (no * trailing underscores) instead - verified against the NDK's * own compiler, which reports __ARM_NEON 1 and no __ARM_NEON__ * for aarch64-linux-android - so the guard was false on every * 64-bit build and NEON was never reported there. That is why * System Information listed ASIMD alone on a device whose CPU * has had NEON since it was designed. */ cpu |= RETRO_SIMD_NEON; #ifdef CPU_ARM_RUNFAST arm_enable_runfast_mode(); #endif } #elif defined(__ARM_NEON) || defined(__ARM_NEON__) cpu |= RETRO_SIMD_NEON; #ifdef CPU_ARM_RUNFAST arm_enable_runfast_mode(); #endif #elif defined(__ALTIVEC__) cpu |= RETRO_SIMD_VMX; #elif defined(XBOX360) cpu |= RETRO_SIMD_VMX128; #elif defined(PSP) || defined(PS2) cpu |= RETRO_SIMD_VFPU; #elif defined(GEKKO) cpu |= RETRO_SIMD_PS; #endif return cpu; } /* The probe is not cheap: on x86 it issues a chain of serialising CPUID * instructions, and on ARM/Linux it opens and line-parses /proc/cpuinfo * once per feature queried. The answer cannot change over the lifetime * of the process, so probe once and hand out the cached mask thereafter. * Callers that sit on a per-frame or per-asset path (the image decoders * select their SIMD kernels this way) were paying the full probe on every * single call. */ uint64_t cpu_features_get(void) { /* The mask is published with a release store and consumed with an * acquire load, so a thread that observes the ready flag is * guaranteed to see the fully written mask. * * The probe being idempotent is not on its own enough: if several * threads reach it before any has published, they all write * cpu_features_cache with plain stores, and those writes are * unordered with respect to one another. Same value or not, that * is a data race and ThreadSanitizer reports it. Elect exactly one * writer with an atomic increment; the threads that lose return the * value they computed themselves, which is identical, so nobody * spins and nobody writes memory another thread is writing. */ static uint64_t cpu_features_cache; static retro_atomic_int_t cpu_features_claim; /* 0 = unclaimed */ static retro_atomic_int_t cpu_features_ready; /* 0 = not probed yet */ uint64_t cpu; if (retro_atomic_load_acquire_int(&cpu_features_ready)) return cpu_features_cache; cpu = cpu_features_probe(); if (retro_atomic_fetch_add_int(&cpu_features_claim, 1) == 0) { cpu_features_cache = cpu; retro_atomic_store_release_int(&cpu_features_ready, 1); } return cpu; } void cpu_features_get_model_name(char *s, int len) { #if defined(CPU_X86) && !defined(__MACH__) union { int32_t i[4]; uint32_t u[4]; uint8_t s[16]; } flags; int i, j; int pos = 0; bool start = false; if (!s) return; x86_cpuid(0x80000000, flags.i); /* Check for additional cpuid attributes availability */ if (flags.u[0] < 0x80000004) return; for (i = 0; i < 3; i++) { memset(flags.i, 0, sizeof(flags.i)); x86_cpuid(0x80000002 + i, flags.i); for (j = 0; j < (int)sizeof(flags.s); j++) { if (!start && flags.s[j] == ' ') continue; start = true; if (pos == len - 1) { /* truncate if we ran out of room */ s[pos] = '\0'; goto end; } s[pos++] = flags.s[j]; } } end: /* terminate our string */ if (pos < len) s[pos] = '\0'; #elif defined(__MACH__) if (!s) return; { size_t __len = len; sysctlbyname("machdep.cpu.brand_string", s, &__len, NULL, 0); } #elif defined(__linux__) if (!s) return; { char *model_name, line[128]; FILE *fp = fopen("/proc/cpuinfo", "r"); if (!fp) return; while (fgets(line, sizeof(line), fp)) { if (strncmp(line, "model name", 10)) continue; if ((model_name = strstr(line + 10, ": "))) { model_name += 2; strlcpy(s, model_name, len); } break; } fclose(fp); #if defined(WEBOS) struct stat st; if (stat("/usr/bin/lscpu", &st) == 0) { FILE *pipe = popen("/usr/bin/lscpu", "r"); if (pipe) { char buf[256]; while (fgets(buf, sizeof(buf), pipe)) { if (strncmp(buf, "Model name:", 11) == 0) { const char *p = strchr(buf, ':'); if (p) { size_t len2; p++; /* skip ':' */ while (*p == ' ' || *p == '\t') p++; len2 = strcspn(p, "\r\n"); if (len2 > 0) { char *tmp = malloc(len2 + 1); if (tmp) { memcpy(tmp, p, len2); tmp[len2] = '\0'; if (s[0] != '\0') { size_t oldlen = strlen(s); char *combined = (char*)malloc(oldlen + len2 + 4); if (combined) { memcpy(combined, s, oldlen); combined[oldlen] = ' '; combined[oldlen + 1] = '('; memcpy(combined + oldlen + 2, tmp, len2); combined[oldlen + 2 + len2] = ')'; combined[oldlen + 2 + len2 + 1] = '\0'; strlcpy(s, combined, len); free(combined); } } else strlcpy(s, tmp, len); free(tmp); } } } break; } } pclose(pipe); } } #endif } #endif } /* Sleep until cpu_features_get_time_usec() reads at least @deadline. * It lives here, beside the clock it is measured on, and nowhere a * launcher compiles: the salamanders build rtime.c for localtime alone * and never link this file. * * Absolute where the platform offers it, so the time between reading * the clock and entering the kernel, and any early or interrupted * wake, are not added to when the caller comes back. Never early * against that clock on any backend; late as every sleep may be, and * a caller that needs the instant itself sleeps short and spins the * rest, as the frame limiter does with its measured margin. */ #if (defined(__linux__) || defined(ANDROID)) && !defined(__MACH__) /* The exact tool: the clock above is clock_gettime(CLOCK_MONOTONIC) * here, and clock_nanosleep() takes an absolute deadline on the same * clock. EINTR re-arms against the unchanged deadline by definition * of TIMER_ABSTIME. */ void retro_sleep_until_us(retro_time_t deadline) { struct timespec ts; ts.tv_sec = (time_t)(deadline / 1000000); ts.tv_nsec = (long)((deadline % 1000000) * 1000); while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) != 0) ; } #elif defined(__APPLE__) && defined(__MACH__) #include /* mach_wait_until() is the absolute wait, on the Mach clock; the * public microsecond clock above is CLOCK_MONOTONIC, which Darwin * derives from the same hardware ticks. The two are bridged with one * paired read - nanoseconds apart - and unlike a relative sleep the * arming is still absolute in Mach terms, so an early wake or the * kernel's leeway re-arms nothing and accumulates nothing. */ void retro_sleep_until_us(retro_time_t deadline) { static mach_timebase_info_data_t tb; uint64_t ticks; retro_time_t now = cpu_features_get_time_usec(); uint64_t mach_now = mach_absolute_time(); if (deadline <= now) return; /* A constant; a racing first read fills in the same values. */ if (!tb.denom) mach_timebase_info(&tb); ticks = (uint64_t)(deadline - now) * 1000 * tb.denom / tb.numer; mach_wait_until(mach_now + ticks); } #else /* Windows and everything else: re-arm the platform's best relative * wait - retro_sleep_us from retro_timers.h, included above on every * platform - against the deadline until the clock agrees. On desktop * Windows that wait is rtime.c's per-thread high-resolution timer, so * each lap is microsecond-grained; on a platform whose sleep rounds * up the loop ends one lap past the deadline, no worse than the * relative call was. */ void retro_sleep_until_us(retro_time_t deadline) { for (;;) { retro_time_t now = cpu_features_get_time_usec(); if (now >= deadline) return; retro_sleep_us((unsigned)(deadline - now)); } } #endif