SDL: Add loongarch support

From 17f63e53f83e6cef3490d5edc8a24d5b22bbabe1 Mon Sep 17 00:00:00 2001
From: yuanhecai <[EMAIL REDACTED]>
Date: Tue, 31 May 2022 19:10:05 +0800
Subject: [PATCH] Add loongarch support

LSX and LASX are enabled by default if compiler supports them.
---
 configure                 | 127 ++++++++++++++++++++++++++++++++++++++
 configure.ac              |  64 +++++++++++++++++++
 include/SDL_config.h.in   |   2 +
 include/SDL_cpuinfo.h     |  30 +++++++++
 src/cpuinfo/SDL_cpuinfo.c |  45 ++++++++++++++
 5 files changed, 268 insertions(+)

diff --git a/configure b/configure
index 7032f7c718a..121593e1681 100755
--- a/configure
+++ b/configure
@@ -838,6 +838,8 @@ enable_3dnow
 enable_sse
 enable_sse2
 enable_sse3
+enable_lsx
+enable_lasx
 enable_altivec
 enable_oss
 enable_alsa
@@ -1620,6 +1622,8 @@ Optional Features:
   --enable-sse            use SSE assembly routines [default=yes]
   --enable-sse2           use SSE2 assembly routines [default=maybe]
   --enable-sse3           use SSE3 assembly routines [default=maybe]
+  --enable-lsx            use LSX assembly routines [default=yes]
+  --enable-lasx           use LASX assembly routines [default=yes]
   --enable-altivec        use Altivec assembly routines [default=yes]
   --enable-oss            support the OSS audio API [default=maybe]
   --enable-alsa           support the ALSA audio API [default=yes]
@@ -18769,6 +18773,129 @@ $as_echo "#define HAVE_IMMINTRIN_H 1" >>confdefs.h
 
     fi
 
+    # Check whether --enable-lsx was given.
+if test "${enable_lsx+set}" = set; then :
+  enableval=$enable_lsx;
+else
+  enable_lsx=yes
+fi
+
+    if test x$enable_lsx = xyes; then
+        save_CFLAGS="$CFLAGS"
+        have_gcc_lsx=no
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GCC -mlsx option" >&5
+$as_echo_n "checking for GCC -mlsx option... " >&6; }
+        lsx_CFLAGS="-mlsx"
+        CFLAGS="$save_CFLAGS $lsx_CFLAGS"
+
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        #ifndef __loongarch_sx
+        #error Assembler CPP flag not enabled
+        #endif
+
+int
+main ()
+{
+
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+        have_gcc_lsx=yes
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_lsx" >&5
+$as_echo "$have_gcc_lsx" >&6; }
+        CFLAGS="$save_CFLAGS"
+
+        if test x$have_gcc_lsx = xyes; then
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $lsx_CFLAGS"
+            SUMMARY_math="${SUMMARY_math} lsx"
+        fi
+    fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "lsxintrin.h" "ac_cv_header_lsxintrin_h" "$ac_includes_default"
+if test "x$ac_cv_header_lsxintrin_h" = xyes; then :
+  have_lsx_h_hdr=yes
+else
+  have_lsx_h_hdr=no
+fi
+
+
+    if test x$have_lsx_h_hdr = xyes; then
+
+$as_echo "#define HAVE_LSXINTRIN_H 1" >>confdefs.h
+
+    fi
+
+
+    # Check whether --enable-lasx was given.
+if test "${enable_lasx+set}" = set; then :
+  enableval=$enable_lasx;
+else
+  enable_LASX=yes
+fi
+
+    if test x$enable_LASX = xyes; then
+        save_CFLAGS="$CFLAGS"
+        have_gcc_lasx=no
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GCC -mlasx option" >&5
+$as_echo_n "checking for GCC -mlasx option... " >&6; }
+        lasx_CFLAGS="-mlasx"
+        CFLAGS="$save_CFLAGS $lasx_CFLAGS"
+
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        #ifndef __loongarch_asx
+        #error Assembler CPP flag not enabled
+        #endif
+
+int
+main ()
+{
+
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+        have_gcc_lasx=yes
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_gcc_lasx" >&5
+$as_echo "$have_gcc_lasx" >&6; }
+        CFLAGS="$save_CFLAGS"
+
+        if test x$have_gcc_lasx = xyes; then
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $lasx_CFLAGS"
+            SUMMARY_math="${SUMMARY_math} lasx"
+        fi
+    fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "lasxintrin.h" "ac_cv_header_lasxintrin_h" "$ac_includes_default"
+if test "x$ac_cv_header_lasxintrin_h" = xyes; then :
+  have_lasx_h_hdr=yes
+else
+  have_lasx_h_hdr=no
+fi
+
+
+    if test x$have_lasx_h_hdr = xyes; then
+
+$as_echo "#define HAVE_LASXINTRIN_H 1" >>confdefs.h
+
+    fi
+
     # Check whether --enable-altivec was given.
 if test "${enable_altivec+set}" = set; then :
   enableval=$enable_altivec;
diff --git a/configure.ac b/configure.ac
index 8bba0af0c4c..421ea98ce91 100644
--- a/configure.ac
+++ b/configure.ac
@@ -852,6 +852,70 @@ dnl Check for various instruction support
     fi
 fi
 
+    AC_ARG_ENABLE(lsx,
+[AC_HELP_STRING([--enable-lsx], [use LSX assembly routines [default=yes]])],
+                  , enable_lsx=yes)
+    if test x$enable_lsx = xyes; then
+        save_CFLAGS="$CFLAGS"
+        have_gcc_lsx=no
+        AC_MSG_CHECKING(for GCC -mlsx option)
+        lsx_CFLAGS="-mlsx"
+        CFLAGS="$save_CFLAGS $lsx_CFLAGS"
+
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+        #ifndef __loongarch_sx
+        #error Assembler CPP flag not enabled
+        #endif
+        ]], [])], [have_gcc_lsx=yes], [])
+        AC_MSG_RESULT($have_gcc_lsx)
+        CFLAGS="$save_CFLAGS"
+
+        if test x$have_gcc_lsx = xyes; then
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $lsx_CFLAGS"
+            SUMMARY_math="${SUMMARY_math} lsx"
+        fi
+    fi
+
+    AC_MSG_CHECKING(for lsxintrin.h)
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <lsxintrin.h>]])],
+       [have_lsxintrin_h_hdr=yes],[have_lsxintrin_h_hdr=no])
+    AC_MSG_RESULT($have_lsxintrin_h_hdr)
+    if test x$have_lsxintrin_h_hdr = xyes; then
+        AC_DEFINE(HAVE_LSXINTRIN_H, 1, [ ])
+    fi
+
+    AC_ARG_ENABLE(lasx,
+[AC_HELP_STRING([--enable-lasx], [use LASX assembly routines [default=yes]])],
+                  , enable_LASX=yes)
+    if test x$enable_LASX = xyes; then
+        save_CFLAGS="$CFLAGS"
+        have_gcc_lasx=no
+        AC_MSG_CHECKING(for GCC -mlasx option)
+        lasx_CFLAGS="-mlasx"
+        CFLAGS="$save_CFLAGS $lasx_CFLAGS"
+
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+        #ifndef __loongarch_asx
+        #error Assembler CPP flag not enabled
+        #endif
+        ]], [])], [have_gcc_lasx=yes], [])
+        AC_MSG_RESULT($have_gcc_lasx)
+        CFLAGS="$save_CFLAGS"
+
+        if test x$have_gcc_lasx = xyes; then
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $lasx_CFLAGS"
+            SUMMARY_math="${SUMMARY_math} lasx"
+        fi
+    fi
+
+    AC_MSG_CHECKING(for lasxintrin.h)
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <lasxintrin.h>]])],
+       [have_lasxintrin_h_hdr=yes],[have_lasxintrin_h_hdr=no])
+    AC_MSG_RESULT($have_lasxintrin_h_hdr)
+    if test x$have_lasxintrin_h_hdr = xyes; then
+        AC_DEFINE(HAVE_LASXINTRIN_H, 1, [ ])
+    fi
+
 dnl See if the OSS audio interface is supported
 CheckOSS()
 {
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index cd322ce879e..9a80d45b7c0 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -229,6 +229,8 @@
 #undef HAVE_LIBUDEV_H
 #undef HAVE_LIBSAMPLERATE_H
 #undef HAVE_LIBDECOR_H
+#undef HAVE_LSXINTRIN_H
+#undef HAVE_LASXINTRIN_H
 
 #undef HAVE_DDRAW_H
 #undef HAVE_DINPUT_H
diff --git a/include/SDL_cpuinfo.h b/include/SDL_cpuinfo.h
index 55050da26dc..e1069ed6c63 100644
--- a/include/SDL_cpuinfo.h
+++ b/include/SDL_cpuinfo.h
@@ -98,6 +98,14 @@ _m_prefetch(void *__P)
 #if defined(__3dNOW__) && !defined(SDL_DISABLE_MM3DNOW_H)
 #include <mm3dnow.h>
 #endif
+#if defined(__loongarch_sx) && !defined(SDL_DISABLE_LSX_H)
+#include <lsxintrin.h>
+#define __LSX__
+#endif
+#if defined(__loongarch_asx) && !defined(SDL_DISABLE_LASX_H)
+#include <lasxintrin.h>
+#define __LASX__
+#endif
 #if defined(HAVE_IMMINTRIN_H) && !defined(SDL_DISABLE_IMMINTRIN_H)
 #include <immintrin.h>
 #else
@@ -433,6 +441,28 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasARMSIMD(void);
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_HasNEON(void);
 
+/**
+ * Determine whether the CPU has LSX (LOONGARCH SIMD) features.
+ *
+ * This always returns false on CPUs that aren't using LOONGARCH instruction sets.
+ *
+ * \returns SDL_TRUE if the CPU has LOONGARCH LSX features or SDL_FALSE if not.
+ *
+ * \since This function is available since SDL 2.0.23.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasLSX(void);
+
+/**
+ * Determine whether the CPU has LASX (LOONGARCH SIMD) features.
+ *
+ * This always returns false on CPUs that aren't using LOONGARCH instruction sets.
+ *
+ * \returns SDL_TRUE if the CPU has LOONGARCH LASX features or SDL_FALSE if not.
+ *
+ * \since This function is available since SDL 2.0.23.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasLASX(void);
+
 /**
  * Get the amount of RAM configured in the system.
  *
diff --git a/src/cpuinfo/SDL_cpuinfo.c b/src/cpuinfo/SDL_cpuinfo.c
index aa994357aa4..9f2101cf21a 100644
--- a/src/cpuinfo/SDL_cpuinfo.c
+++ b/src/cpuinfo/SDL_cpuinfo.c
@@ -112,6 +112,12 @@
 #define CPU_HAS_NEON    (1 << 11)
 #define CPU_HAS_AVX512F (1 << 12)
 #define CPU_HAS_ARM_SIMD (1 << 13)
+#define CPU_HAS_LSX     (1 << 14)
+#define CPU_HAS_LASX    (1 << 15)
+
+#define CPU_CFG2 0x2
+#define CPU_CFG2_LSX    (1 << 6)
+#define CPU_CFG2_LASX   (1 << 7)
 
 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__ && !__FreeBSD__
 /* This is the brute force way of detecting instruction sets...
@@ -508,6 +514,23 @@ CPU_haveNEON(void)
 #endif
 }
 
+static int
+CPU_readCPUCFG(void)
+{
+    uint32_t cfg2 = 0;
+#if defined __loongarch__
+    __asm__ volatile(
+        "cpucfg %0, %1 \n\t"
+        : "+&r"(cfg2)
+        : "r"(CPU_CFG2)
+    );
+#endif
+    return cfg2;
+}
+
+#define CPU_haveLSX() (CPU_readCPUCFG() & CPU_CFG2_LSX)
+#define CPU_haveLASX() (CPU_readCPUCFG() & CPU_CFG2_LASX)
+
 #if defined(__e2k__)
 inline int
 CPU_have3DNow(void)
@@ -885,6 +908,14 @@ SDL_GetCPUFeatures(void)
             SDL_CPUFeatures |= CPU_HAS_NEON;
             SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
+	if (CPU_haveLSX()) {
+            SDL_CPUFeatures |= CPU_HAS_LSX;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
+        }
+	if (CPU_haveLASX()) {
+            SDL_CPUFeatures |= CPU_HAS_LASX;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 32);
+        }
     }
     return SDL_CPUFeatures;
 }
@@ -974,6 +1005,18 @@ SDL_HasNEON(void)
     return CPU_FEATURE_AVAILABLE(CPU_HAS_NEON);
 }
 
+SDL_bool
+SDL_HasLSX(void)
+{
+    return CPU_FEATURE_AVAILABLE(CPU_HAS_LSX);
+}
+
+SDL_bool
+SDL_HasLASX(void)
+{
+    return CPU_FEATURE_AVAILABLE(CPU_HAS_LASX);
+}
+
 static int SDL_SystemRAM = 0;
 
 int
@@ -1170,6 +1213,8 @@ main()
     printf("AVX-512F: %d\n", SDL_HasAVX512F());
     printf("ARM SIMD: %d\n", SDL_HasARMSIMD());
     printf("NEON: %d\n", SDL_HasNEON());
+    printf("LSX: %d\n", SDL_HasLSX());
+    printf("LASX: %d\n", SDL_HasLASX());
     printf("RAM: %d MB\n", SDL_GetSystemRAM());
     return 0;
 }