Commit 35430a73f251f6bb7554d7e2cc5af26dee5a46d5

Ryan C. Gordon 2016-11-17T01:15:16

cpuinfo: first attempt at SDL_HasNEON() implementation.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54a23f0..2945e49 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -615,7 +615,7 @@ if(LIBC)
             _uitoa _ultoa strtol strtoul _i64toa _ui64toa strtoll strtoull
             atoi atof strcmp strncmp _stricmp strcasecmp _strnicmp strncasecmp
             vsscanf vsnprintf fseeko fseeko64 sigaction setjmp
-            nanosleep sysconf sysctlbyname
+            nanosleep sysconf sysctlbyname getauxval
             )
       string(TOUPPER ${_FN} _UPPER)
       set(_HAVEVAR "HAVE_${_UPPER}")
diff --git a/configure.in b/configure.in
index 748ec03..801d2f1 100644
--- a/configure.in
+++ b/configure.in
@@ -268,7 +268,7 @@ if test x$enable_libc = xyes; then
         AC_DEFINE(HAVE_MPROTECT, 1, [ ])
         ]),
     )
-    AC_CHECK_FUNCS(malloc calloc realloc free getenv setenv putenv unsetenv qsort abs bcopy memset memcpy memmove strlen strlcpy strlcat strdup _strrev _strupr _strlwr strchr strrchr strstr itoa _ltoa _uitoa _ultoa strtol strtoul _i64toa _ui64toa strtoll strtoull atoi atof strcmp strncmp _stricmp strcasecmp _strnicmp strncasecmp vsscanf vsnprintf fseeko fseeko64 sigaction setjmp nanosleep sysconf sysctlbyname)
+    AC_CHECK_FUNCS(malloc calloc realloc free getenv setenv putenv unsetenv qsort abs bcopy memset memcpy memmove strlen strlcpy strlcat strdup _strrev _strupr _strlwr strchr strrchr strstr itoa _ltoa _uitoa _ultoa strtol strtoul _i64toa _ui64toa strtoll strtoull atoi atof strcmp strncmp _stricmp strcasecmp _strnicmp strncasecmp vsscanf vsnprintf fseeko fseeko64 sigaction setjmp nanosleep sysconf sysctlbyname getauxval)
 
     AC_CHECK_LIB(m, pow, [LIBS="$LIBS -lm"; EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lm"])
     AC_CHECK_FUNCS(atan atan2 acos asin ceil copysign cos cosf fabs floor log pow scalbn sin sinf sqrt sqrtf tan tanf)
diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake
index 12692f5..dd49bdc 100644
--- a/include/SDL_config.h.cmake
+++ b/include/SDL_config.h.cmake
@@ -171,6 +171,8 @@
 #cmakedefine HAVE_PTHREAD_SETNAME_NP 1
 #cmakedefine HAVE_PTHREAD_SET_NAME_NP 1
 #cmakedefine HAVE_SEM_TIMEDWAIT 1
+#cmakedefine HAVE_GETAUXVAL 1
+
 #elif __WIN32__
 #cmakedefine HAVE_STDARG_H 1
 #cmakedefine HAVE_STDDEF_H 1
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index 6b96b4f..90a304a 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -173,6 +173,7 @@
 #undef HAVE_PTHREAD_SETNAME_NP
 #undef HAVE_PTHREAD_SET_NAME_NP
 #undef HAVE_SEM_TIMEDWAIT
+#undef HAVE_GETAUXVAL
 
 #else
 #define HAVE_STDARG_H   1
diff --git a/include/SDL_cpuinfo.h b/include/SDL_cpuinfo.h
index d0ba47b..1a5524b 100644
--- a/include/SDL_cpuinfo.h
+++ b/include/SDL_cpuinfo.h
@@ -145,6 +145,11 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX(void);
 extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX2(void);
 
 /**
+ *  This function returns true if the CPU has NEON (ARM SIMD) features.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasNEON(void);
+
+/**
  *  This function returns the amount of RAM configured in the system, in MB.
  */
 extern DECLSPEC int SDLCALL SDL_GetSystemRAM(void);
diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c
index f2b7f7e..9462875 100644
--- a/src/cpuinfo/SDL_cpuinfo.c
+++ b/src/cpuinfo/SDL_cpuinfo.c
@@ -50,6 +50,15 @@
 #include <setjmp.h>
 #endif
 
+#if defined(__ANDROID__)
+#include <cpu-features.h>
+#endif
+
+#if defined(__LINUX__) && HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#include <asm/hwcap.h>
+#endif
+
 #define CPU_HAS_RDTSC   0x00000001
 #define CPU_HAS_ALTIVEC 0x00000002
 #define CPU_HAS_MMX     0x00000004
@@ -61,6 +70,7 @@
 #define CPU_HAS_SSE42   0x00000200
 #define CPU_HAS_AVX     0x00000400
 #define CPU_HAS_AVX2    0x00000800
+#define CPU_HAS_NEON    0x00001000
 
 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
 /* This is the brute force way of detecting instruction sets...
@@ -289,6 +299,40 @@ CPU_haveAltiVec(void)
 }
 
 static int
+CPU_haveNEON(void)
+{
+    int neon = 0;
+
+/* The way you detect NEON is a privileged instruction on ARM, so you have
+   query the OS kernel in a platform-specific way. :/ */
+#ifndef SDL_CPUINFO_DISABLED
+#if defined(__APPLE__) && defined(__ARM_ARCH)
+    /* all hardware that runs iOS 5 and later support NEON, but check anyhow */
+    size_t length = sizeof (neon);
+    const int error = sysctlbyname("hw.optional.neon", &neon, &length, NULL, 0);
+    if (!error)
+        neon = (neon != 0);
+#elif defined(__ANDROID__)
+    if ( (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM) &&
+         ((android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0) ) {
+        neon = 1;
+    }
+#elif defined(__LINUX__) && HAVE_GETAUXVAL && defined(__arm__)
+    if (getauxval(AT_HWCAP) & HWCAP_NEON) {
+        neon = 1;
+    }
+#elif (defined(__WINDOWS__) || defined(__WINRT__)) && defined(_M_ARM)
+    /* All WinRT ARM devices are required to support NEON, but just in case. */
+    if (IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)) {
+        neon = 1;
+    }
+#endif
+#endif
+
+    return neon;
+}
+
+static int
 CPU_have3DNow(void)
 {
     if (CPU_CPUIDMaxFunction > 0) {  /* that is, do we have CPUID at all? */
@@ -527,6 +571,9 @@ SDL_GetCPUFeatures(void)
         if (CPU_haveAVX2()) {
             SDL_CPUFeatures |= CPU_HAS_AVX2;
         }
+        if (CPU_haveNEON()) {
+            SDL_CPUFeatures |= CPU_HAS_NEON;
+        }
     }
     return SDL_CPUFeatures;
 }
@@ -598,6 +645,12 @@ SDL_HasAVX2(void)
     return CPU_FEATURE_AVAILABLE(CPU_HAS_AVX2);
 }
 
+SDL_bool
+SDL_HasNEON(void)
+{
+    return CPU_FEATURE_AVAILABLE(CPU_HAS_NEON);
+}
+
 static int SDL_SystemRAM = 0;
 
 int
@@ -667,6 +720,7 @@ main()
     printf("SSE4.2: %d\n", SDL_HasSSE42());
     printf("AVX: %d\n", SDL_HasAVX());
     printf("AVX2: %d\n", SDL_HasAVX2());
+    printf("NEON: %d\n", SDL_HasNEON());
     printf("RAM: %d MB\n", SDL_GetSystemRAM());
     return 0;
 }
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index d0d9e18..3b02257 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -621,3 +621,4 @@
 #define SDL_GameControllerGetVendor SDL_GameControllerGetVendor_REAL
 #define SDL_GameControllerGetProduct SDL_GameControllerGetProduct_REAL
 #define SDL_GameControllerGetProductVersion SDL_GameControllerGetProductVersion_REAL
+#define SDL_HasNEON SDL_HasNEON_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 28afcf9..b225d2a 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -653,3 +653,4 @@ SDL_DYNAPI_PROC(Uint16,SDL_JoystickGetProductVersion,(SDL_Joystick *a),(a),retur
 SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetVendor,(SDL_GameController *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetProduct,(SDL_GameController *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetProductVersion,(SDL_GameController *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasNEON,(void),(),return)
diff --git a/test/testplatform.c b/test/testplatform.c
index bbd5b8a..33b94fd 100644
--- a/test/testplatform.c
+++ b/test/testplatform.c
@@ -360,6 +360,7 @@ TestCPUInfo(SDL_bool verbose)
         SDL_Log("SSE4.2 %s\n", SDL_HasSSE42()? "detected" : "not detected");
         SDL_Log("AVX %s\n", SDL_HasAVX()? "detected" : "not detected");
         SDL_Log("AVX2 %s\n", SDL_HasAVX2()? "detected" : "not detected");
+        SDL_Log("NEON %s\n", SDL_HasNEON()? "detected" : "not detected");
         SDL_Log("System RAM %d MB\n", SDL_GetSystemRAM());
     }
     return (0);