Merge v0.9.0 into `main`. am: 3de70d0029 am: f66f3d320a am: d64300359d am: cc2079932d am: 085bd65bfb

Original change: https://android-review.googlesource.com/c/platform/external/cpu_features/+/2758973

Change-Id: I6578d89d868af43a81addeb3d6558993cc4626fd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.dockerignore b/.dockerignore
index 65950ea..5169bae 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,8 +1,4 @@
 # Project Files unneeded by docker
-cmake/ci/Makefile
-cmake/ci/docker
-cmake/ci/doc
-cmake/ci/cache
 .git
 .gitignore
 .github
@@ -16,6 +12,15 @@
 LICENSE
 README.md
 
+bazel/ci/Makefile
+bazel/ci/docker
+bazel/ci/doc
+
+cmake/ci/Makefile
+cmake/ci/docker
+cmake/ci/doc
+cmake/ci/cache
+
 build/
 cmake_build/
 build_cross/
diff --git a/.github/workflows/Dockerfile b/.github/workflows/Dockerfile
index 8d6f3dc..28371cb 100644
--- a/.github/workflows/Dockerfile
+++ b/.github/workflows/Dockerfile
@@ -3,3 +3,4 @@
 FROM alpine:edge
 # Install system build dependencies
 RUN apk add --no-cache git clang-extra-tools
+RUN git config --global --add safe.directory /repo
diff --git a/.github/workflows/aarch64_linux_bazel.yml b/.github/workflows/aarch64_linux_bazel.yml
new file mode 100644
index 0000000..72dbfff
--- /dev/null
+++ b/.github/workflows/aarch64_linux_bazel.yml
@@ -0,0 +1,26 @@
+name: AArch64 Linux Bazel
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  bazel:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check out repository code
+        uses: actions/checkout@v3
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+      - name: Check docker
+        run: |
+          docker info
+          docker buildx ls
+      - name: Build
+        run: make --directory=bazel/ci arm64_build
+      - name: Test
+        run: make --directory=bazel/ci arm64_test
diff --git a/.github/workflows/amd64_freebsd_cmake.yml b/.github/workflows/amd64_freebsd_cmake.yml
index 8bad4f8..37c95d7 100644
--- a/.github/workflows/amd64_freebsd_cmake.yml
+++ b/.github/workflows/amd64_freebsd_cmake.yml
@@ -11,7 +11,7 @@
   # Only MacOS hosted runner provides virtualisation with vagrant/virtualbox installed.
   # see: https://github.com/actions/virtual-environments/tree/main/images/macos
   make:
-    runs-on: macos-10.15
+    runs-on: macos-latest
     steps:
     - uses: actions/checkout@v2
     - name: vagrant version
diff --git a/.github/workflows/amd64_linux_bazel.yml b/.github/workflows/amd64_linux_bazel.yml
index 599faf5..fcb0ce4 100644
--- a/.github/workflows/amd64_linux_bazel.yml
+++ b/.github/workflows/amd64_linux_bazel.yml
@@ -13,14 +13,14 @@
     runs-on: ubuntu-latest
     steps:
       - name: Check out repository code
-        uses: actions/checkout@v2
-      - name: Install Bazel
+        uses: actions/checkout@v3
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+      - name: Check docker
         run: |
-          curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
-          sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
-          echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
-          sudo apt-get update
-          sudo apt-get install bazel
-          bazel --version
+          docker info
+          docker buildx ls
+      - name: Build
+        run: make --directory=bazel/ci amd64_build
       - name: Test
-        run: bazel test -s --verbose_failures //...
+        run: make --directory=bazel/ci amd64_test
diff --git a/.github/workflows/amd64_macos_bazel.yml b/.github/workflows/amd64_macos_bazel.yml
new file mode 100644
index 0000000..fd1bc5a
--- /dev/null
+++ b/.github/workflows/amd64_macos_bazel.yml
@@ -0,0 +1,35 @@
+name: amd64 MacOS Bazel
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  bazel:
+    runs-on: macos-latest
+    steps:
+      - name: Check out repository code
+        uses: actions/checkout@v3
+      - name: Install Bazel
+        run: |
+          brew update
+          brew unlink bazelisk
+          brew install bazel
+      - name: Check Bazel
+        run: bazel version
+      - name: Build
+        run: >
+          bazel build
+          -c opt
+          --subcommands=true
+          ...
+      - name: Test
+        run: >
+          bazel test
+          -c opt
+          --test_output=errors
+          ...
diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml
index 1afd391..2ab0de0 100644
--- a/.github/workflows/clang_format.yml
+++ b/.github/workflows/clang_format.yml
@@ -7,7 +7,7 @@
   clang-format:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Fetch origin/main
       run: git fetch origin main
     - name: List of changed file(s)
diff --git a/BUILD.bazel b/BUILD.bazel
index 116ef6a..6925944 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,7 +1,8 @@
 # cpu_features, a cross platform C99 library to get cpu features at runtime.
 
 load("@bazel_skylib//lib:selects.bzl", "selects")
-load("//:bazel/platforms.bzl", "PLATFORM_CPU_ARM", "PLATFORM_CPU_ARM64", "PLATFORM_CPU_MIPS", "PLATFORM_CPU_PPC", "PLATFORM_CPU_X86_64")
+load("//:bazel/platforms.bzl", "PLATFORM_CPU_ARM", "PLATFORM_CPU_ARM64", "PLATFORM_CPU_MIPS", "PLATFORM_CPU_PPC", "PLATFORM_CPU_RISCV32", "PLATFORM_CPU_RISCV64", "PLATFORM_CPU_X86_64")
+load("//:bazel/platforms.bzl", "PLATFORM_OS_MACOS")
 
 package(
     default_visibility = ["//visibility:public"],
@@ -170,7 +171,10 @@
     name = "hwcaps",
     srcs = ["src/hwcaps.c"],
     copts = C99_FLAGS,
-    defines = ["HAVE_STRONG_GETAUXVAL"],
+    defines = selects.with_or({
+        PLATFORM_OS_MACOS: ["HAVE_DLFCN_H"],
+        "//conditions:default": ["HAVE_STRONG_GETAUXVAL"],
+    }),
     includes = INCLUDES,
     textual_hdrs = ["include/internal/hwcaps.h"],
     deps = [
@@ -213,15 +217,18 @@
             "src/impl_x86_windows.c",
         ],
         PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
-        PLATFORM_CPU_ARM64: ["src/impl_aarch64_linux_or_android.c"],
+        PLATFORM_CPU_ARM64: [
+            "src/impl_aarch64_linux_or_android.c",
+            "src/impl_aarch64_macos_or_iphone.c",
+            "src/impl_aarch64_windows.c",
+        ],
         PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
         PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
+        PLATFORM_CPU_RISCV32: ["src/impl_riscv_linux.c"],
+        PLATFORM_CPU_RISCV64: ["src/impl_riscv_linux.c"],
     }),
-    copts = C99_FLAGS,
-    includes = INCLUDES,
-    textual_hdrs = selects.with_or({
+    hdrs = selects.with_or({
         PLATFORM_CPU_X86_64: [
-            "src/impl_x86__base_implementation.inl",
             "include/cpuinfo_x86.h",
             "include/internal/cpuid_x86.h",
             "include/internal/windows_utils.h",
@@ -230,6 +237,19 @@
         PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
         PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
         PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
+        PLATFORM_CPU_RISCV32: ["include/cpuinfo_riscv.h"],
+        PLATFORM_CPU_RISCV64: ["include/cpuinfo_riscv.h"],
+    }),
+    copts = C99_FLAGS,
+    defines = selects.with_or({
+        PLATFORM_OS_MACOS: ["HAVE_SYSCTLBYNAME"],
+        "//conditions:default": [],
+    }),
+    includes = INCLUDES,
+    textual_hdrs = selects.with_or({
+        PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
+        PLATFORM_CPU_ARM64: ["src/impl_aarch64__base_implementation.inl"],
+        "//conditions:default": [],
     }) + [
         "src/define_introspection.inl",
         "src/define_introspection_and_hwcaps.inl",
@@ -257,9 +277,15 @@
             "src/impl_x86_windows.c",
         ],
         PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
-        PLATFORM_CPU_ARM64: ["src/impl_aarch64_linux_or_android.c"],
+        PLATFORM_CPU_ARM64: [
+            "src/impl_aarch64_linux_or_android.c",
+            "src/impl_aarch64_macos_or_iphone.c",
+            "src/impl_aarch64_windows.c",
+        ],
         PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
         PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
+        PLATFORM_CPU_RISCV32: ["src/impl_riscv_linux.c"],
+        PLATFORM_CPU_RISCV64: ["src/impl_riscv_linux.c"],
     }),
     hdrs = selects.with_or({
         PLATFORM_CPU_X86_64: [
@@ -271,15 +297,21 @@
         PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
         PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
         PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
+        PLATFORM_CPU_RISCV32: ["include/cpuinfo_riscv.h"],
+        PLATFORM_CPU_RISCV64: ["include/cpuinfo_riscv.h"],
     }),
     copts = C99_FLAGS,
     defines = selects.with_or({
         PLATFORM_CPU_X86_64: ["CPU_FEATURES_MOCK_CPUID_X86"],
         "//conditions:default": [],
+    }) + selects.with_or({
+        PLATFORM_OS_MACOS: ["HAVE_SYSCTLBYNAME"],
+        "//conditions:default": [],
     }),
     includes = INCLUDES,
     textual_hdrs = selects.with_or({
         PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
+        PLATFORM_CPU_ARM64: ["src/impl_aarch64__base_implementation.inl"],
         "//conditions:default": [],
     }) + [
         "src/define_introspection.inl",
@@ -304,6 +336,8 @@
         PLATFORM_CPU_ARM: ["test/cpuinfo_arm_test.cc"],
         PLATFORM_CPU_MIPS: ["test/cpuinfo_mips_test.cc"],
         PLATFORM_CPU_PPC: ["test/cpuinfo_ppc_test.cc"],
+        PLATFORM_CPU_RISCV32: ["test/cpuinfo_riscv_test.cc"],
+        PLATFORM_CPU_RISCV64: ["test/cpuinfo_riscv_test.cc"],
         PLATFORM_CPU_X86_64: ["test/cpuinfo_x86_test.cc"],
     }),
     includes = INCLUDES,
@@ -327,3 +361,18 @@
         ":cpuinfo",
     ],
 )
+
+cc_library(
+    name = "ndk_compat",
+    srcs = ["ndk_compat/cpu-features.c"],
+    copts = C99_FLAGS,
+    includes = INCLUDES + ["ndk_compat"],
+    textual_hdrs = ["ndk_compat/cpu-features.h"],
+    deps = [
+        ":cpu_features_macros",
+        ":cpuinfo",
+        ":filesystem",
+        ":stack_line_reader",
+        ":string_view",
+    ],
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bcc9bb0..0454967 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.13)
 
 # option() honors normal variables.
 # see: https://cmake.org/cmake/help/git-stage/policy/CMP0077.html
@@ -6,10 +6,18 @@
   cmake_policy(SET CMP0077 NEW)
 endif()
 
-project(CpuFeatures VERSION 0.8.0 LANGUAGES C)
+project(CpuFeatures VERSION 0.9.0 LANGUAGES C)
 
 set(CMAKE_C_STANDARD 99)
 
+# when cpu_features is included as subproject (i.e. using add_subdirectory(cpu_features))
+# in the source tree of a project that uses it, test rules are disabled.
+if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+  option(BUILD_TESTING "Enable test rule" OFF)
+else()
+  option(BUILD_TESTING "Enable test rule" ON)
+endif()
+
 # Default Build Type to be Release
 if(NOT CMAKE_BUILD_TYPE)
   set(CMAKE_BUILD_TYPE "Release" CACHE STRING
@@ -17,6 +25,22 @@
       FORCE)
 endif(NOT CMAKE_BUILD_TYPE)
 
+# An option to enable/disable the executable target list_cpu_features.
+# Disable it by default if the project is included as a subproject.
+if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+  option(BUILD_EXECUTABLE "Build list_cpu_features executable." OFF)
+else()
+  option(BUILD_EXECUTABLE "Build list_cpu_features executable." ON)
+endif()
+
+# An option which allows to switch off install steps. Useful for embedding.
+# Disable it by default if the project is included as a subproject.
+if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+  option(ENABLE_INSTALL "Enable install targets" OFF)
+else()
+  option(ENABLE_INSTALL "Enable install targets" ON)
+endif()
+
 # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
 # it prominent in the GUI.
 # cpu_features uses bit-fields which are - to some extends - implementation-defined (see https://en.cppreference.com/w/c/language/bit_field).
@@ -51,14 +75,15 @@
 set(PROCESSOR_IS_POWER FALSE)
 set(PROCESSOR_IS_S390X FALSE)
 set(PROCESSOR_IS_RISCV FALSE)
+set(PROCESSOR_IS_LOONGARCH FALSE)
 
 if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
   set(PROCESSOR_IS_MIPS TRUE)
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(^aarch64)|(^arm64)|(^ARM64)")
   set(PROCESSOR_IS_AARCH64 TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
   set(PROCESSOR_IS_ARM TRUE)
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64|amd64)|(^i.86$)")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(x86_64)|(AMD64|amd64)|(^i.86$)")
   set(PROCESSOR_IS_X86 TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
   set(PROCESSOR_IS_POWER TRUE)
@@ -66,6 +91,8 @@
   set(PROCESSOR_IS_S390X TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv")
   set(PROCESSOR_IS_RISCV TRUE)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^loongarch")
+  set(PROCESSOR_IS_LOONGARCH TRUE)
 endif()
 
 macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
@@ -90,6 +117,8 @@
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_s390x.h)
   elseif(PROCESSOR_IS_RISCV)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_riscv.h)
+  elseif(PROCESSOR_IS_LOONGARCH)
+      list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_loongarch.h)
   else()
     message(FATAL_ERROR "Unsupported architectures ${CMAKE_SYSTEM_PROCESSOR}")
   endif()
@@ -147,20 +176,21 @@
 target_include_directories(cpu_features
   PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpu_features>
 )
-if(PROCESSOR_IS_X86)
-  if(APPLE)
-    target_compile_definitions(cpu_features PRIVATE HAVE_SYSCTLBYNAME)
-  endif()
+
+if(APPLE)
+  target_compile_definitions(cpu_features PRIVATE HAVE_SYSCTLBYNAME)
 endif()
-add_library(CpuFeature::cpu_features ALIAS cpu_features)
+add_library(CpuFeatures::cpu_features ALIAS cpu_features)
 
 #
 # program : list_cpu_features
 #
 
-add_executable(list_cpu_features ${PROJECT_SOURCE_DIR}/src/utils/list_cpu_features.c)
-target_link_libraries(list_cpu_features PRIVATE cpu_features)
-add_executable(CpuFeature::list_cpu_features ALIAS list_cpu_features)
+if(BUILD_EXECUTABLE)
+  add_executable(list_cpu_features ${PROJECT_SOURCE_DIR}/src/utils/list_cpu_features.c)
+  target_link_libraries(list_cpu_features PRIVATE cpu_features)
+  add_executable(CpuFeatures::list_cpu_features ALIAS list_cpu_features)
+endif()
 
 #
 # ndk_compat
@@ -226,36 +256,47 @@
 #
 # Install cpu_features and list_cpu_features
 #
-
-include(GNUInstallDirs)
-install(TARGETS cpu_features list_cpu_features
-  EXPORT CpuFeaturesTargets
-  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
-install(EXPORT CpuFeaturesTargets
-  NAMESPACE CpuFeatures::
-  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures
-  COMPONENT Devel
-)
-include(CMakePackageConfigHelpers)
-configure_package_config_file(cmake/CpuFeaturesConfig.cmake.in
-  "${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
-  INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
-  NO_SET_AND_CHECK_MACRO
-  NO_CHECK_REQUIRED_COMPONENTS_MACRO
-)
-write_basic_package_version_file(
-  "${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
-  COMPATIBILITY SameMajorVersion
-)
-install(
-  FILES
+if(ENABLE_INSTALL)
+  include(GNUInstallDirs)
+  install(TARGETS cpu_features
+    EXPORT CpuFeaturesTargets
+    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
+  )
+  if(BUILD_EXECUTABLE)
+    install(TARGETS list_cpu_features
+      EXPORT CpuFeaturesTargets
+      PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
+      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+      BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+  endif()
+  install(EXPORT CpuFeaturesTargets
+    NAMESPACE CpuFeatures::
+    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures
+    COMPONENT Devel
+  )
+  include(CMakePackageConfigHelpers)
+  configure_package_config_file(cmake/CpuFeaturesConfig.cmake.in
     "${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
+    INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
+    NO_SET_AND_CHECK_MACRO
+    NO_CHECK_REQUIRED_COMPONENTS_MACRO
+  )
+  write_basic_package_version_file(
     "${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
-  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
-  COMPONENT Devel
-)
+    COMPATIBILITY SameMajorVersion
+  )
+  install(
+    FILES
+      "${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
+      "${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
+    DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
+    COMPONENT Devel
+  )
+endif()
diff --git a/METADATA b/METADATA
index ab7bdf7..ceb1712 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
     type: GIT
     value: "https://github.com/google/cpu_features.git"
   }
-  version: "v0.8.0"
+  version: "v0.9.0"
   license_type: NOTICE
   last_upgrade_date {
     year: 2023
-    month: 4
-    day: 27
+    month: 9
+    day: 14
   }
 }
diff --git a/README.md b/README.md
index ee0ce70..4e66497 100644
--- a/README.md
+++ b/README.md
@@ -7,36 +7,44 @@
 
 [comment]: <> (The following lines are generated by "scripts/generate_badges.d" that you can run online https://run.dlang.io/)
 
-| Os | amd64 | AArch64 | ARM | MIPS | POWER | RISCV | s390x |
-| :-- | --: | --: | --: | --: | --: | --: | --: |
-| Linux | [![][i1a0]][l1a0]<br/>[![][i1a1]][l1a1] | [![][i1b0]][l1b0]<br/>![][d1] | [![][i1c0]][l1c0]<br/>![][d1] | [![][i1d0]][l1d0]<br/>![][d1] | [![][i1e0]][l1e0]<br/>![][d1] | [![][i1f0]][l1f0]<br/>![][d1] | [![][i1g0]][l1g0]<br/>![][d1] |
-| FreeBSD | [![][i2a0]][l2a0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] |
-| MacOS | [![][i3a0]][l3a0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] |
-| Windows | [![][i4a0]][l4a0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] | ![][d0]<br/>![][d1] |
+|  | Linux | FreeBSD | MacOS | Windows |
+| :-- | --: | --: | --: | --: |
+| amd64 | [![CMake][i1a0]][l1a0]<br/>[![Bazel][i1a1]][l1a1] | [![CMake][i2a0]][l2a0]<br/>![Bazel][d1] | [![CMake][i3a0]][l3a0]<br/>[![Bazel][i3a1]][l3a1] | [![CMake][i4a0]][l4a0]<br/>![Bazel][d1] |
+| AArch64 | [![CMake][i1b0]][l1b0]<br/>[![Bazel][i1b1]][l1b1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| ARM | [![CMake][i1c0]][l1c0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| MIPS | [![CMake][i1d0]][l1d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| POWER | [![CMake][i1e0]][l1e0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| RISCV | [![CMake][i1f0]][l1f0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| LOONGARCH | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
+| s390x | [![CMake][i1h0]][l1h0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
 
-[d0]: https://img.shields.io/badge/CMake-N%2FA-lightgrey
-[d1]: https://img.shields.io/badge/Bazel-N%2FA-lightgrey
-[i1a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_cmake.yml?branch=main&label=CMake
-[i1a1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_bazel.yml?branch=main&label=Bazel
-[i1b0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/aarch64_linux_cmake.yml?branch=main&label=CMake
-[i1c0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/arm_linux_cmake.yml?branch=main&label=CMake
-[i1d0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/mips_linux_cmake.yml?branch=main&label=CMake
-[i1e0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/power_linux_cmake.yml?branch=main&label=CMake
-[i1f0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/riscv_linux_cmake.yml?branch=main&label=CMake
-[i1g0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/s390x_linux_cmake.yml?branch=main&label=CMake
-[i2a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_freebsd_cmake.yml?branch=main&label=CMake
-[i3a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_macos_cmake.yml?branch=main&label=CMake
-[i4a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_windows_cmake.yml?branch=main&label=CMake
+[d0]: https://img.shields.io/badge/n%2Fa-lightgrey?&logo=cmake
+[d1]: https://img.shields.io/badge/n%2Fa-lightgrey?&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
+[i1a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1a1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
+[i1b0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/aarch64_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1b1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/aarch64_linux_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
+[i1c0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/arm_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1d0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/mips_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1e0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/power_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1f0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/riscv_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i1h0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/s390x_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i2a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_freebsd_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i3a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_macos_cmake.yml?branch=main&event=push&label=&logo=cmake
+[i3a1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_macos_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
+[i4a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_windows_cmake.yml?branch=main&event=push&label=&logo=cmake
 [l1a0]: https://github.com/google/cpu_features/actions/workflows/amd64_linux_cmake.yml
 [l1a1]: https://github.com/google/cpu_features/actions/workflows/amd64_linux_bazel.yml
 [l1b0]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux_cmake.yml
+[l1b1]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux_bazel.yml
 [l1c0]: https://github.com/google/cpu_features/actions/workflows/arm_linux_cmake.yml
 [l1d0]: https://github.com/google/cpu_features/actions/workflows/mips_linux_cmake.yml
 [l1e0]: https://github.com/google/cpu_features/actions/workflows/power_linux_cmake.yml
 [l1f0]: https://github.com/google/cpu_features/actions/workflows/riscv_linux_cmake.yml
-[l1g0]: https://github.com/google/cpu_features/actions/workflows/s390x_linux_cmake.yml
+[l1h0]: https://github.com/google/cpu_features/actions/workflows/s390x_linux_cmake.yml
 [l2a0]: https://github.com/google/cpu_features/actions/workflows/amd64_freebsd_cmake.yml
 [l3a0]: https://github.com/google/cpu_features/actions/workflows/amd64_macos_cmake.yml
+[l3a1]: https://github.com/google/cpu_features/actions/workflows/amd64_macos_bazel.yml
 [l4a0]: https://github.com/google/cpu_features/actions/workflows/amd64_windows_cmake.yml
 
 ## Table of Contents
@@ -178,14 +186,14 @@
 <a name="support"></a>
 ## What's supported
 
-|         | x86³ | AArch64 | ARM     | MIPS⁴   | POWER   | RISCV   | s390x   |
-|---------|:----:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
-| Linux   | yes² | yes¹    | yes¹    | yes¹    | yes¹    | yes¹    | yes¹    |
-| FreeBSD | yes² | not yet | not yet | not yet | not yet | N/A     | not yet |
-| MacOs   | yes² | not yet | N/A     | N/A     | no      | N/A     | no      |
-| Windows | yes² | not yet | not yet | N/A     | N/A     | N/A     | N/A     |
-| Android | yes² | yes¹    | yes¹    | yes¹    | N/A     | N/A     | N/A     |
-| iOS     | N/A  | not yet | not yet | N/A     | N/A     | N/A     | N/A     |
+|         | x86³ | AArch64 | ARM     | MIPS⁴   | POWER   | RISCV   | Loongarch | s390x   |
+|---------|:----:|:-------:|:-------:|:-------:|:-------:|:-------:|:---------:|:-------:|
+| Linux   | yes² | yes¹    | yes¹    | yes¹    | yes¹    | yes¹    | yes¹      | yes¹    |
+| FreeBSD | yes² | not yet | not yet | not yet | not yet | N/A     | not yet   | not yet |
+| MacOs   | yes² | yes⁵    | N/A     | N/A     | N/A     | N/A     | N/A       | N/A     |
+| Windows | yes² | not yet | not yet | N/A     | N/A     | N/A     | N/A       | N/A     |
+| Android | yes² | yes¹    | yes¹    | yes¹    | N/A     | N/A     | N/A       | N/A     |
+| iOS     | N/A  | not yet | not yet | N/A     | N/A     | N/A     | N/A       | N/A     |
 
 1.  **Features revealed from Linux.** We gather data from several sources
     depending on availability:
@@ -200,6 +208,8 @@
     microarchitecture allows the client to reject particular microarchitectures.
 4.  All flavors of Mips are supported, little and big endian as well as 32/64
     bits.
+5.  **Features revealed from sysctl.** features are retrieved by the `sysctl`
+    instruction.
 
 <a name="ndk"></a>
 ## Android NDK's drop in replacement
diff --git a/bazel/ci/Makefile b/bazel/ci/Makefile
new file mode 100644
index 0000000..1967213
--- /dev/null
+++ b/bazel/ci/Makefile
@@ -0,0 +1,122 @@
+PROJECT := cpu_features
+BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
+SHA1 := $(shell git rev-parse --verify HEAD)
+
+# General commands
+.PHONY: help
+BOLD=\e[1m
+RESET=\e[0m
+
+help:
+	@echo -e "${BOLD}SYNOPSIS${RESET}"
+	@echo -e "\tmake <target> [NOCACHE=1]"
+	@echo
+	@echo -e "${BOLD}DESCRIPTION${RESET}"
+	@echo -e "\ttest build inside docker container to have a reproductible build."
+	@echo
+	@echo -e "${BOLD}MAKE TARGETS${RESET}"
+	@echo -e "\t${BOLD}help${RESET}: display this help and exit."
+	@echo
+	@echo -e "\t${BOLD}<platform>_<stage>${RESET}: build <stage> docker image using an Ubuntu:latest x86_64 base image."
+	@echo -e "\t${BOLD}save_<platform>_<stage>${RESET}: Save the <stage> docker image."
+	@echo -e "\t${BOLD}sh_<platform>_<stage>${RESET}: run a container using the <stage> docker image (debug purpose)."
+	@echo -e "\t${BOLD}clean_<platform>_<stage>${RESET}: Remove cache and docker image."
+	@echo
+	@echo -e "\tWith ${BOLD}<platform>${RESET}:"
+	@echo -e "\t\t${BOLD}amd64${RESET} (linux/amd64)"
+	@echo -e "\t\t${BOLD}arm64${RESET} (linux/arm64)"
+	@echo
+	@echo -e "\tWith ${BOLD}<stage>${RESET}:"
+	@echo -e "\t\t${BOLD}env${RESET}"
+	@echo -e "\t\t${BOLD}devel${RESET}"
+	@echo -e "\t\t${BOLD}build${RESET}"
+	@echo -e "\t\t${BOLD}test${RESET}"
+	@echo -e "\te.g. 'make amd64_build'"
+	@echo
+	@echo -e "\t${BOLD}clean${RESET}: Remove cache and ALL docker images."
+	@echo
+	@echo -e "\t${BOLD}NOCACHE=1${RESET}: use 'docker build --no-cache' when building container (default use cache)."
+	@echo -e "\t${BOLD}VERBOSE=1${RESET}: use 'docker build --progress=plain' when building container."
+	@echo
+	@echo -e "branch: $(BRANCH)"
+	@echo -e "sha1: $(SHA1)"
+
+# Need to add cmd_platform to PHONY otherwise target are ignored since they do not
+# contain recipe (using FORCE do not work here)
+.PHONY: all
+all: build
+
+# Delete all implicit rules to speed up makefile
+MAKEFLAGS += --no-builtin-rules
+.SUFFIXES:
+# Remove some rules from gmake that .SUFFIXES does not remove.
+SUFFIXES =
+# Keep all intermediate files
+# ToDo: try to remove it later
+.SECONDARY:
+
+# Docker image name prefix.
+IMAGE := ${PROJECT}
+
+DOCKER_BUILDX_CMD := docker buildx build
+ifdef NOCACHE
+DOCKER_BUILDX_CMD := ${DOCKER_BUILDX_CMD} --no-cache
+endif
+ifdef VERBOSE
+DOCKER_BUILDX_CMD := ${DOCKER_BUILDX_CMD} --progress=plain
+endif
+DOCKER_RUN_CMD := docker run --rm --init --net=host
+
+############
+## NATIVE ##
+############
+# ref: https://go.dev/doc/install/source#environment
+# ref: https://github.com/containerd/containerd/blob/269548fa27e0089a8b8278fc4fc781d7f65a939b/platforms/platforms.go#L80-L94
+PLATFORMS := amd64 arm64
+STAGES := env devel build test
+
+define make-platform-stage-target =
+$1_$2: docker/Dockerfile
+	${DOCKER_BUILDX_CMD} \
+ --platform linux/$1 \
+ --build-arg PLATFORM="$1" \
+ --target=$2 \
+ --tag ${IMAGE}:$1_$2 \
+ -f $$< ../..
+
+save_$1_$2: cache/$1/docker_$2.tar
+cache/$1/docker_$2.tar: $1_$2
+	@rm -f $$@
+	mkdir -p cache/$1
+	docker save ${IMAGE}:$1_$2 -o $$@
+
+sh_$1_$2: $1_$2
+	${DOCKER_RUN_CMD} --platform linux/$1 -it --name ${IMAGE}_$1_$2 ${IMAGE}:$1_$2
+
+clean_$1_$2:
+	docker image rm -f ${IMAGE}:$1_$2 2>/dev/null
+	rm -f cache/$1/docker_$2.tar
+endef
+
+define make-platform-target =
+$(foreach stage,$(STAGES),$(eval $(call make-platform-stage-target,$1,$(stage))))
+
+# merge
+.PHONY: clean_$1
+clean_$1: $(addprefix clean_$1_, $(STAGES))
+	-rmdir cache/$1
+endef
+
+$(foreach platform,$(PLATFORMS),$(eval $(call make-platform-target,$(platform))))
+
+## MERGE ##
+.PHONY: clean
+clean: $(addprefix clean_, $(PLATFORMS))
+	docker container prune -f
+	docker image prune -f
+	-rmdir cache
+
+.PHONY: distclean
+distclean: clean
+	-docker container rm -f $$(docker container ls -aq)
+	-docker image rm -f $$(docker image ls -aq)
diff --git a/bazel/ci/docker/Dockerfile b/bazel/ci/docker/Dockerfile
new file mode 100644
index 0000000..a46b493
--- /dev/null
+++ b/bazel/ci/docker/Dockerfile
@@ -0,0 +1,37 @@
+# Create a virtual environment with all tools installed
+# ref: https://hub.docker.com/_/ubuntu
+FROM ubuntu:latest AS env
+
+# Install system build dependencies
+ENV PATH=/usr/local/bin:$PATH
+RUN apt-get update -qq \
+&& DEBIAN_FRONTEND=noninteractive apt-get install -yq \
+ git wget build-essential \
+&& apt-get clean \
+&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+ENTRYPOINT ["/usr/bin/bash", "-c"]
+CMD ["/usr/bin/bash"]
+
+# Install Bazelisk
+ARG PLATFORM
+RUN wget \
+ "https://github.com/bazelbuild/bazelisk/releases/download/v1.18.0/bazelisk-linux-${PLATFORM}" \
+&& chmod +x "bazelisk-linux-${PLATFORM}" \
+&& mv "bazelisk-linux-${PLATFORM}" /usr/local/bin/bazel
+
+FROM env AS devel
+WORKDIR /home/project
+COPY . .
+
+FROM devel AS build
+RUN bazel version
+RUN bazel build \
+ -c opt \
+ --subcommands=true \
+ ...
+
+FROM build AS test
+RUN bazel test \
+ -c opt \
+ --test_output=errors \
+ ...
diff --git a/bazel/platforms.bzl b/bazel/platforms.bzl
index 5671add..eb025ce 100644
--- a/bazel/platforms.bzl
+++ b/bazel/platforms.bzl
@@ -9,3 +9,10 @@
 PLATFORM_CPU_MIPS = ("@platforms//cpu:mips64")
 
 PLATFORM_CPU_PPC = ("@platforms//cpu:ppc")
+
+PLATFORM_CPU_RISCV32 = ("@platforms//cpu:riscv32")
+
+PLATFORM_CPU_RISCV64 = ("@platforms//cpu:riscv64")
+
+
+PLATFORM_OS_MACOS = ("@platforms//os:macos")
diff --git a/cmake/README.md b/cmake/README.md
index de33b23..feaa2fb 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -17,7 +17,7 @@
 2- You can then use the cmake command `add_subdirectory()` to include
 cpu_features directly and use the `cpu_features` target in your project.
 
-3- Add the `CpuFeature::cpu_features` target to the `target_link_libraries()` section of
+3- Add the `CpuFeatures::cpu_features` target to the `target_link_libraries()` section of
 your executable or of your library.
 
 ## Disabling tests
diff --git a/include/cpu_features_macros.h b/include/cpu_features_macros.h
index 215b567..4fe7c3d 100644
--- a/include/cpu_features_macros.h
+++ b/include/cpu_features_macros.h
@@ -83,6 +83,10 @@
 #define CPU_FEATURES_ARCH_RISCV128
 #endif
 
+#if defined(__loongarch64)
+#define CPU_FEATURES_ARCH_LOONGARCH
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // Os
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/include/cpuinfo_aarch64.h b/include/cpuinfo_aarch64.h
index d124b5f..18a3313 100644
--- a/include/cpuinfo_aarch64.h
+++ b/include/cpuinfo_aarch64.h
@@ -169,6 +169,27 @@
   int ecv : 1;         // Enhanced counter virtualization.
   int afp : 1;         // Alternate floating-point behaviour.
   int rpres : 1;       // 12-bit reciprocal (square root) estimate precision.
+  int mte3 : 1;        // MTE asymmetric fault handling.
+  int sme : 1;         // Scalable Matrix Extension.
+  int smei16i64 : 1;   // 16-bit to 64-bit integer widening outer product.
+  int smef64f64 : 1;   // FP64 to FP64 outer product.
+  int smei8i32 : 1;    // 8-bit to 32-bit integer widening outer product.
+  int smef16f32 : 1;   // FP16 to FP32 outer product.
+  int smeb16f32 : 1;   // BFloat16 to FP32 outper product.
+  int smef32f32 : 1;   // FP32 to FP32 outer product.
+  int smefa64 : 1;     // Full A64 support for SME in streaming mode.
+  int wfxt : 1;        // WFE and WFI with timeout.
+  int ebf16 : 1;       // Extended BFloat16 instructions.
+  int sveebf16 : 1;    // SVE BFloat16 instructions.
+  int cssc : 1;        // Common short sequence compression instructions.
+  int rprfm : 1;       // Range Prefetch Memory hint instruction.
+  int sve2p1 : 1;      // Scalable Vector Extension (version 2.1).
+  int sme2 : 1;        // Scalable Matrix Extension (version 2).
+  int sme2p1 : 1;      // Scalable Matrix Extension (version 2.1).
+  int smei16i32 : 1;   // 16-bit to 64-bit integer widening outer product.
+  int smebi32i32 : 1;  // 1-bit binary to 32-bit integer outer product.
+  int smeb16b16 : 1;   // SME2.1 BFloat16 instructions.
+  int smef16f16 : 1;   // FP16 to FP16 outer product.
 
   // Make sure to update Aarch64FeaturesEnum below if you add a field here.
 } Aarch64Features;
@@ -242,6 +263,27 @@
   AARCH64_ECV,
   AARCH64_AFP,
   AARCH64_RPRES,
+  AARCH64_MTE3,
+  AARCH64_SME,
+  AARCH64_SME_I16I64,
+  AARCH64_SME_F64F64,
+  AARCH64_SME_I8I32,
+  AARCH64_SME_F16F32,
+  AARCH64_SME_B16F32,
+  AARCH64_SME_F32F32,
+  AARCH64_SME_FA64,
+  AARCH64_WFXT,
+  AARCH64_EBF16,
+  AARCH64_SVE_EBF16,
+  AARCH64_CSSC,
+  AARCH64_RPRFM,
+  AARCH64_SVE2P1,
+  AARCH64_SME2,
+  AARCH64_SME2P1,
+  AARCH64_SME_I16I32,
+  AARCH64_SME_BI32I32,
+  AARCH64_SME_B16B16,
+  AARCH64_SME_F16F16,
   AARCH64_LAST_,
 } Aarch64FeaturesEnum;
 
diff --git a/include/cpuinfo_loongarch.h b/include/cpuinfo_loongarch.h
new file mode 100644
index 0000000..d166223
--- /dev/null
+++ b/include/cpuinfo_loongarch.h
@@ -0,0 +1,77 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+#if !defined(CPU_FEATURES_ARCH_LOONGARCH)
+#error "Including cpuinfo_loongarch.h from a non-loongarch target."
+#endif
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+  // Base
+  int CPUCFG : 1;    // Instruction for Identify CPU Features 
+
+  // Extension
+  int LAM : 1;       // Extension for Atomic Memory Access Instructions
+  int UAL : 1;       // Extension for Non-Aligned Memory Access
+  int FPU : 1;       // Extension for Basic Floating-Point Instructions
+  int LSX : 1;       // Extension for Loongson SIMD eXtension
+  int LASX : 1;      // Extension for Loongson Advanced SIMD eXtension
+  int CRC32 : 1;     // Extension for Cyclic Redundancy Check Instructions
+  int COMPLEX : 1;   // Extension for Complex Vector Operation Instructions
+  int CRYPTO : 1;    // Extension for Encryption And Decryption Vector Instructions
+  int LVZ : 1;       // Extension for Virtualization
+  int LBT_X86 : 1;   // Extension for X86 Binary Translation Extension
+  int LBT_ARM : 1;   // Extension for ARM Binary Translation Extension
+  int LBT_MIPS : 1;  // Extension for MIPS Binary Translation Extension
+  int PTW : 1;       // Extension for Page Table Walker
+
+} LoongArchFeatures;
+
+typedef struct {
+  LoongArchFeatures features;
+} LoongArchInfo;
+
+typedef enum {
+  LOONGARCH_CPUCFG,
+  LOONGARCH_LAM,
+  LOONGARCH_UAL,
+  LOONGARCH_FPU,
+  LOONGARCH_LSX,
+  LOONGARCH_LASX,
+  LOONGARCH_CRC32,
+  LOONGARCH_COMPLEX,
+  LOONGARCH_CRYPTO,
+  LOONGARCH_LVZ,
+  LOONGARCH_LBT_X86,
+  LOONGARCH_LBT_ARM,
+  LOONGARCH_LBT_MIPS,
+  LOONGARCH_PTW,
+  LOONGARCH_LAST_,
+} LoongArchFeaturesEnum;
+
+LoongArchInfo GetLoongArchInfo(void);
+int GetLoongArchFeaturesEnumValue(const LoongArchFeatures* features,
+                              LoongArchFeaturesEnum value);
+const char* GetLoongArchFeaturesEnumName(LoongArchFeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif  // CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
diff --git a/include/cpuinfo_x86.h b/include/cpuinfo_x86.h
index e897500..796d3f3 100644
--- a/include/cpuinfo_x86.h
+++ b/include/cpuinfo_x86.h
@@ -86,6 +86,7 @@
   int amx_bf16 : 1;
   int amx_tile : 1;
   int amx_int8 : 1;
+  int amx_fp16 : 1;
 
   int pclmulqdq : 1;
   int smx : 1;
@@ -107,6 +108,9 @@
   int fz_rep_movsb : 1;        // Fast zero-length REP MOVSB
   int fs_rep_stosb : 1;        // Fast short REP STOSB
   int fs_rep_cmpsb_scasb : 1;  // Fast short REP CMPSB/SCASB
+
+  int lam: 1; // Intel Linear Address Mask
+  int uai: 1; // AMD Upper Address Ignore
   // Make sure to update X86FeaturesEnum below if you add a field here.
 } X86Features;
 
@@ -251,6 +255,7 @@
   X86_AMX_BF16,
   X86_AMX_TILE,
   X86_AMX_INT8,
+  X86_AMX_FP16,
   X86_PCLMULQDQ,
   X86_SMX,
   X86_SGX,
@@ -270,6 +275,8 @@
   X86_FZ_REP_MOVSB,
   X86_FS_REP_STOSB,
   X86_FS_REP_CMPSB_SCASB,
+  X86_LAM,
+  X86_UAI,
   X86_LAST_,
 } X86FeaturesEnum;
 
diff --git a/include/internal/hwcaps.h b/include/internal/hwcaps.h
index 3290cc9..59e1657 100644
--- a/include/internal/hwcaps.h
+++ b/include/internal/hwcaps.h
@@ -83,6 +83,27 @@
 #define AARCH64_HWCAP2_ECV (1UL << 19)
 #define AARCH64_HWCAP2_AFP (1UL << 20)
 #define AARCH64_HWCAP2_RPRES (1UL << 21)
+#define AARCH64_HWCAP2_MTE3 (1UL << 22)
+#define AARCH64_HWCAP2_SME (1UL << 23)
+#define AARCH64_HWCAP2_SME_I16I64 (1UL << 24)
+#define AARCH64_HWCAP2_SME_F64F64 (1UL << 25)
+#define AARCH64_HWCAP2_SME_I8I32 (1UL << 26)
+#define AARCH64_HWCAP2_SME_F16F32 (1UL << 27)
+#define AARCH64_HWCAP2_SME_B16F32 (1UL << 28)
+#define AARCH64_HWCAP2_SME_F32F32 (1UL << 29)
+#define AARCH64_HWCAP2_SME_FA64 (1UL << 30)
+#define AARCH64_HWCAP2_WFXT (1UL << 31)
+#define AARCH64_HWCAP2_EBF16 (1UL << 32)
+#define AARCH64_HWCAP2_SVE_EBF16 (1UL << 33)
+#define AARCH64_HWCAP2_CSSC (1UL << 34)
+#define AARCH64_HWCAP2_RPRFM (1UL << 35)
+#define AARCH64_HWCAP2_SVE2P1 (1UL << 36)
+#define AARCH64_HWCAP2_SME2 (1UL << 37)
+#define AARCH64_HWCAP2_SME2P1 (1UL << 38)
+#define AARCH64_HWCAP2_SME_I16I32 (1UL << 39)
+#define AARCH64_HWCAP2_SME_BI32I32 (1UL << 40)
+#define AARCH64_HWCAP2_SME_B16B16 (1UL << 41)
+#define AARCH64_HWCAP2_SME_F16F16 (1UL << 42)
 
 // http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/uapi/asm/hwcap.h
 #define ARM_HWCAP_SWP (1UL << 0)
@@ -216,6 +237,22 @@
 #define RISCV_HWCAP_C (1UL << ('C' - 'A'))
 #define RISCV_HWCAP_V (1UL << ('V' - 'A'))
 
+// https://github.com/torvalds/linux/blob/master/arch/loongarch/include/uapi/asm/hwcap.h
+#define HWCAP_LOONGARCH_CPUCFG   (1 << 0)
+#define HWCAP_LOONGARCH_LAM      (1 << 1)
+#define HWCAP_LOONGARCH_UAL      (1 << 2)
+#define HWCAP_LOONGARCH_FPU      (1 << 3)
+#define HWCAP_LOONGARCH_LSX      (1 << 4)
+#define HWCAP_LOONGARCH_LASX     (1 << 5)
+#define HWCAP_LOONGARCH_CRC32    (1 << 6)
+#define HWCAP_LOONGARCH_COMPLEX  (1 << 7)
+#define HWCAP_LOONGARCH_CRYPTO   (1 << 8)
+#define HWCAP_LOONGARCH_LVZ      (1 << 9)
+#define HWCAP_LOONGARCH_LBT_X86  (1 << 10)
+#define HWCAP_LOONGARCH_LBT_ARM  (1 << 11)
+#define HWCAP_LOONGARCH_LBT_MIPS (1 << 12)
+#define HWCAP_LOONGARCH_PTW      (1 << 13)
+
 typedef struct {
   unsigned long hwcaps;
   unsigned long hwcaps2;
diff --git a/scripts/generate_badges.d b/scripts/generate_badges.d
old mode 100644
new mode 100755
index bf5aa54..e5037ef
--- a/scripts/generate_badges.d
+++ b/scripts/generate_badges.d
@@ -1,11 +1,23 @@
+#!/usr/bin/env -S docker run --rm -v ${PWD}/scripts:/scripts -v ${PWD}/.github/workflows:/.github/workflows dlanguage/dmd dmd -run /scripts/generate_badges.d
+
+// To run this script:
+// cd /path/to/cpu_features
+// ./scripts/generate_badges.d
+
 import std.algorithm : each, map, cartesianProduct, filter, joiner, sort, uniq;
-import std.array : array;
+import std.array;
+import std.base64 : Base64;
 import std.conv : to;
+import std.file : exists;
 import std.format;
 import std.range : chain, only;
 import std.stdio;
+import std.string : representation;
 import std.traits : EnumMembers;
 
+immutable string bazel_svg = `<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6 .16l5.786 5.786L6 11.732.214 5.946 6 .161zM0 6.214V12l5.786 5.786V12L0 6.214zM18 .16l5.786 5.786L18 11.732l-5.786-5.786L18 .161zM24 6.214V12l-5.786 5.786V12L24 6.214zM12 6.16l5.786 5.786L12 17.732l-5.786-5.786L12 6.161zM11.84 18.054v5.785l-5.786-5.785v-5.786l5.785 5.786zM12.16 18.054l5.786-5.786v5.786l-5.785 5.785v-5.785z" stroke="transparent" fill="white"/></svg>`;
+const string bazel_svg_base64 = Base64.encode(representation(bazel_svg));
+
 enum BuildSystem
 {
     CMake,
@@ -20,6 +32,7 @@
     MIPS,
     POWER,
     RISCV,
+    LOONGARCH,
     s390x,
 }
 
@@ -39,14 +52,11 @@
     Os os;
     BuildSystem build_system;
 
+private:
     string id()
     {
-        return format("%d%c%d", cast(uint)(os) + 1, cast(char)('a' + cpu), cast(uint)(build_system));
-    }
-
-    string disabled_image_ref()
-    {
-        return format("[d%d]", cast(uint)(build_system));
+        return format("%d%c%d", cast(uint)(os) + 1, cast(char)('a' + cpu),
+            cast(uint)(build_system));
     }
 
     string link_ref()
@@ -59,29 +69,6 @@
         return format("[i%s]", id());
     }
 
-    bool enabled()
-    {
-        final switch (build_system)
-        {
-        case BuildSystem.CMake:
-            return os == Os.Linux || cpu == Cpu.amd64;
-        case BuildSystem.Bazel:
-            return os == Os.Linux && cpu == Cpu.amd64;
-        }
-    }
-
-    string text()
-    {
-        if (enabled())
-            return format("[![]%s]%s", image_ref, link_ref);
-        return format("![]%s", disabled_image_ref);
-    }
-
-    string disabled_image_link()
-    {
-        return format("%s: https://img.shields.io/badge/%s-N%%2FA-lightgrey", disabled_image_ref, build_system);
-    }
-
     string filename()
     {
         import std.uni : toLower;
@@ -89,50 +76,87 @@
         return toLower(format("%s_%s_%s.yml", cpu, os, build_system));
     }
 
+    bool enabled()
+    {
+        return exists("../.github/workflows/" ~ filename());
+    }
+
+    string append_logo(string url)
+    {
+        final switch (build_system)
+        {
+        case BuildSystem.CMake:
+            return url ~ "&logo=cmake";
+        case BuildSystem.Bazel:
+            return url ~ "&logo=data:image/svg%2bxml;base64," ~ bazel_svg_base64;
+        }
+    }
+
+public:
+
+    string disabled_image_ref()
+    {
+        return format("[d%d]", cast(uint)(build_system));
+    }
+
+    string text()
+    {
+        if (enabled())
+            return format("[![%s]%s]%s", build_system, image_ref, link_ref);
+        return format("![%s]%s", build_system, disabled_image_ref);
+    }
+
+    string disabled_image_link()
+    {
+        return append_logo(format("%s: https://img.shields.io/badge/n%%2Fa-lightgrey?",
+                disabled_image_ref));
+    }
+
     string link_decl()
     {
-        return format("%s: https://github.com/google/cpu_features/actions/workflows/%s", link_ref, filename());
+        return format("%s: https://github.com/google/cpu_features/actions/workflows/%s",
+            link_ref, filename());
     }
 
     string image_decl()
     {
-        return format(
-            "%s: https://img.shields.io/github/actions/workflow/status/google/cpu_features/%s?branch=main&label=%s", image_ref, filename(), build_system);
+        return append_logo(format("%s: https://img.shields.io/github/actions/workflow/status/google/cpu_features/%s?branch=main&event=push&label=",
+                image_ref, filename()));
     }
+
 }
 
-auto tableHeader(in Cpu[] cpus)
+auto tableHeader(in Os[] oses)
 {
-    return chain(only("Os"), cpus.map!(to!string)).array;
+    return chain(only(""), oses.map!(to!string)).array;
 }
 
-auto tableAlignment(in Cpu[] cpus)
+auto tableAlignment(in Os[] oses)
 {
-    return chain(only(":--"), cpus.map!(v => "--:")).array;
+    return chain(only(":--"), oses.map!(v => "--:")).array;
 }
 
 auto tableCell(Range)(in Os os, in Cpu cpu, Range badges)
 {
-    return badges
-        .filter!(b => b.cpu == cpu && b.os == os)
+    return badges.filter!(b => b.cpu == cpu && b.os == os)
         .map!(b => b.text())
-        .joiner("<br/>")
-        .to!string;
+        .joiner("<br/>").to!string;
 }
 
-auto tableRow(Range)(in Os os, in Cpu[] cpus, Range badges)
+auto tableRow(Range)(in Cpu cpu, in Os[] oses, Range badges)
 {
-    return chain(only(os.to!string), cpus.map!(cpu => tableCell(os, cpu, badges))).array;
+    return chain(only(cpu.to!string), oses.map!(os => tableCell(os, cpu, badges))).array;
 }
 
 auto tableRows(Range)(in Os[] oses, in Cpu[] cpus, Range badges)
 {
-    return oses.map!(os => tableRow(os, cpus, badges)).array;
+    return cpus.map!(cpu => tableRow(cpu, oses, badges)).array;
 }
 
 auto table(Range)(in Os[] oses, in Cpu[] cpus, Range badges)
 {
-    return chain(only(tableHeader(cpus)), only(tableAlignment(cpus)), tableRows(oses, cpus, badges));
+    return chain(only(tableHeader(oses)), only(tableAlignment(oses)),
+        tableRows(oses, cpus, badges));
 }
 
 void main()
@@ -140,26 +164,17 @@
     immutable allCpus = [EnumMembers!Cpu];
     immutable allOses = [EnumMembers!Os];
     immutable allBuildSystems = [EnumMembers!BuildSystem];
-
     auto badges = cartesianProduct(allCpus, allOses, allBuildSystems).map!(
         t => Badge(t[0], t[1], t[2]));
-
     writefln("%(|%-( %s |%) |\n%) |", table(allOses, allCpus, badges));
     writeln();
-    badges
-        .filter!(b => !b.enabled)
+    badges.filter!(b => !b.enabled)
         .map!(b => b.disabled_image_link())
         .array
         .sort
         .uniq
         .each!writeln;
-
-    badges
-        .filter!(b => b.enabled)
+    badges.filter!(b => b.enabled)
         .map!(b => [b.link_decl(), b.image_decl()])
-        .joiner()
-        .array
-        .sort
-        .uniq
-        .each!writeln;
+        .joiner().array.sort.uniq.each!writeln;
 }
diff --git a/src/impl_aarch64__base_implementation.inl b/src/impl_aarch64__base_implementation.inl
new file mode 100644
index 0000000..ccc6c11
--- /dev/null
+++ b/src/impl_aarch64__base_implementation.inl
@@ -0,0 +1,118 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdbool.h>
+
+#include "cpu_features_macros.h"
+#include "cpuinfo_aarch64.h"
+#include "internal/bit_utils.h"
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+#if !defined(CPU_FEATURES_ARCH_AARCH64)
+#error "Cannot compile aarch64_base on a non aarch64 platform."
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                                                  \
+  LINE(AARCH64_FP, fp, "fp", AARCH64_HWCAP_FP, 0)                            \
+  LINE(AARCH64_ASIMD, asimd, "asimd", AARCH64_HWCAP_ASIMD, 0)                \
+  LINE(AARCH64_EVTSTRM, evtstrm, "evtstrm", AARCH64_HWCAP_EVTSTRM, 0)        \
+  LINE(AARCH64_AES, aes, "aes", AARCH64_HWCAP_AES, 0)                        \
+  LINE(AARCH64_PMULL, pmull, "pmull", AARCH64_HWCAP_PMULL, 0)                \
+  LINE(AARCH64_SHA1, sha1, "sha1", AARCH64_HWCAP_SHA1, 0)                    \
+  LINE(AARCH64_SHA2, sha2, "sha2", AARCH64_HWCAP_SHA2, 0)                    \
+  LINE(AARCH64_CRC32, crc32, "crc32", AARCH64_HWCAP_CRC32, 0)                \
+  LINE(AARCH64_ATOMICS, atomics, "atomics", AARCH64_HWCAP_ATOMICS, 0)        \
+  LINE(AARCH64_FPHP, fphp, "fphp", AARCH64_HWCAP_FPHP, 0)                    \
+  LINE(AARCH64_ASIMDHP, asimdhp, "asimdhp", AARCH64_HWCAP_ASIMDHP, 0)        \
+  LINE(AARCH64_CPUID, cpuid, "cpuid", AARCH64_HWCAP_CPUID, 0)                \
+  LINE(AARCH64_ASIMDRDM, asimdrdm, "asimdrdm", AARCH64_HWCAP_ASIMDRDM, 0)    \
+  LINE(AARCH64_JSCVT, jscvt, "jscvt", AARCH64_HWCAP_JSCVT, 0)                \
+  LINE(AARCH64_FCMA, fcma, "fcma", AARCH64_HWCAP_FCMA, 0)                    \
+  LINE(AARCH64_LRCPC, lrcpc, "lrcpc", AARCH64_HWCAP_LRCPC, 0)                \
+  LINE(AARCH64_DCPOP, dcpop, "dcpop", AARCH64_HWCAP_DCPOP, 0)                \
+  LINE(AARCH64_SHA3, sha3, "sha3", AARCH64_HWCAP_SHA3, 0)                    \
+  LINE(AARCH64_SM3, sm3, "sm3", AARCH64_HWCAP_SM3, 0)                        \
+  LINE(AARCH64_SM4, sm4, "sm4", AARCH64_HWCAP_SM4, 0)                        \
+  LINE(AARCH64_ASIMDDP, asimddp, "asimddp", AARCH64_HWCAP_ASIMDDP, 0)        \
+  LINE(AARCH64_SHA512, sha512, "sha512", AARCH64_HWCAP_SHA512, 0)            \
+  LINE(AARCH64_SVE, sve, "sve", AARCH64_HWCAP_SVE, 0)                        \
+  LINE(AARCH64_ASIMDFHM, asimdfhm, "asimdfhm", AARCH64_HWCAP_ASIMDFHM, 0)    \
+  LINE(AARCH64_DIT, dit, "dit", AARCH64_HWCAP_DIT, 0)                        \
+  LINE(AARCH64_USCAT, uscat, "uscat", AARCH64_HWCAP_USCAT, 0)                \
+  LINE(AARCH64_ILRCPC, ilrcpc, "ilrcpc", AARCH64_HWCAP_ILRCPC, 0)            \
+  LINE(AARCH64_FLAGM, flagm, "flagm", AARCH64_HWCAP_FLAGM, 0)                \
+  LINE(AARCH64_SSBS, ssbs, "ssbs", AARCH64_HWCAP_SSBS, 0)                    \
+  LINE(AARCH64_SB, sb, "sb", AARCH64_HWCAP_SB, 0)                            \
+  LINE(AARCH64_PACA, paca, "paca", AARCH64_HWCAP_PACA, 0)                    \
+  LINE(AARCH64_PACG, pacg, "pacg", AARCH64_HWCAP_PACG, 0)                    \
+  LINE(AARCH64_DCPODP, dcpodp, "dcpodp", 0, AARCH64_HWCAP2_DCPODP)           \
+  LINE(AARCH64_SVE2, sve2, "sve2", 0, AARCH64_HWCAP2_SVE2)                   \
+  LINE(AARCH64_SVEAES, sveaes, "sveaes", 0, AARCH64_HWCAP2_SVEAES)           \
+  LINE(AARCH64_SVEPMULL, svepmull, "svepmull", 0, AARCH64_HWCAP2_SVEPMULL)   \
+  LINE(AARCH64_SVEBITPERM, svebitperm, "svebitperm", 0,                      \
+       AARCH64_HWCAP2_SVEBITPERM)                                            \
+  LINE(AARCH64_SVESHA3, svesha3, "svesha3", 0, AARCH64_HWCAP2_SVESHA3)       \
+  LINE(AARCH64_SVESM4, svesm4, "svesm4", 0, AARCH64_HWCAP2_SVESM4)           \
+  LINE(AARCH64_FLAGM2, flagm2, "flagm2", 0, AARCH64_HWCAP2_FLAGM2)           \
+  LINE(AARCH64_FRINT, frint, "frint", 0, AARCH64_HWCAP2_FRINT)               \
+  LINE(AARCH64_SVEI8MM, svei8mm, "svei8mm", 0, AARCH64_HWCAP2_SVEI8MM)       \
+  LINE(AARCH64_SVEF32MM, svef32mm, "svef32mm", 0, AARCH64_HWCAP2_SVEF32MM)   \
+  LINE(AARCH64_SVEF64MM, svef64mm, "svef64mm", 0, AARCH64_HWCAP2_SVEF64MM)   \
+  LINE(AARCH64_SVEBF16, svebf16, "svebf16", 0, AARCH64_HWCAP2_SVEBF16)       \
+  LINE(AARCH64_I8MM, i8mm, "i8mm", 0, AARCH64_HWCAP2_I8MM)                   \
+  LINE(AARCH64_BF16, bf16, "bf16", 0, AARCH64_HWCAP2_BF16)                   \
+  LINE(AARCH64_DGH, dgh, "dgh", 0, AARCH64_HWCAP2_DGH)                       \
+  LINE(AARCH64_RNG, rng, "rng", 0, AARCH64_HWCAP2_RNG)                       \
+  LINE(AARCH64_BTI, bti, "bti", 0, AARCH64_HWCAP2_BTI)                       \
+  LINE(AARCH64_MTE, mte, "mte", 0, AARCH64_HWCAP2_MTE)                       \
+  LINE(AARCH64_ECV, ecv, "ecv", 0, AARCH64_HWCAP2_ECV)                       \
+  LINE(AARCH64_AFP, afp, "afp", 0, AARCH64_HWCAP2_AFP)                       \
+  LINE(AARCH64_RPRES, rpres, "rpres", 0, AARCH64_HWCAP2_RPRES)               \
+  LINE(AARCH64_MTE3, mte3, "mte3", 0, AARCH64_HWCAP2_MTE3)                   \
+  LINE(AARCH64_SME, sme, "sme", 0, AARCH64_HWCAP2_SME)                       \
+  LINE(AARCH64_SME_I16I64, smei16i64, "smei16i64", 0,                        \
+       AARCH64_HWCAP2_SME_I16I64)                                            \
+  LINE(AARCH64_SME_F64F64, smef64f64, "smef64f64", 0,                        \
+       AARCH64_HWCAP2_SME_F64F64)                                            \
+  LINE(AARCH64_SME_I8I32, smei8i32, "smei8i32", 0, AARCH64_HWCAP2_SME_I8I32) \
+  LINE(AARCH64_SME_F16F32, smef16f32, "smef16f32", 0,                        \
+       AARCH64_HWCAP2_SME_F16F32)                                            \
+  LINE(AARCH64_SME_B16F32, smeb16f32, "smeb16f32", 0,                        \
+       AARCH64_HWCAP2_SME_B16F32)                                            \
+  LINE(AARCH64_SME_F32F32, smef32f32, "smef32f32", 0,                        \
+       AARCH64_HWCAP2_SME_F32F32)                                            \
+  LINE(AARCH64_SME_FA64, smefa64, "smefa64", 0, AARCH64_HWCAP2_SME_FA64)     \
+  LINE(AARCH64_WFXT, wfxt, "wfxt", 0, AARCH64_HWCAP2_WFXT)                   \
+  LINE(AARCH64_EBF16, ebf16, "ebf16", 0, AARCH64_HWCAP2_EBF16)               \
+  LINE(AARCH64_SVE_EBF16, sveebf16, "sveebf16", 0, AARCH64_HWCAP2_SVE_EBF16) \
+  LINE(AARCH64_CSSC, cssc, "cssc", 0, AARCH64_HWCAP2_CSSC)                   \
+  LINE(AARCH64_RPRFM, rprfm, "rprfm", 0, AARCH64_HWCAP2_RPRFM)               \
+  LINE(AARCH64_SVE2P1, sve2p1, "sve2p1", 0, AARCH64_HWCAP2_SVE2P1)           \
+  LINE(AARCH64_SME2, sme2, "sme2", 0, AARCH64_HWCAP2_SME2)                   \
+  LINE(AARCH64_SME2P1, sme2p1, "sme2p1", 0, AARCH64_HWCAP2_SME2P1)           \
+  LINE(AARCH64_SME_I16I32, smei16i32, "smei16i32", 0,                        \
+       AARCH64_HWCAP2_SME_I16I32)                                            \
+  LINE(AARCH64_SME_BI32I32, smebi32i32, "smebi32i32", 0,                     \
+       AARCH64_HWCAP2_SME_BI32I32)                                           \
+  LINE(AARCH64_SME_B16B16, smeb16b16, "smeb16b16", 0,                        \
+       AARCH64_HWCAP2_SME_B16B16)                                            \
+  LINE(AARCH64_SME_F16F16, smef16f16, "smef16f16", 0, AARCH64_HWCAP2_SME_F16F16)
+#define INTROSPECTION_PREFIX Aarch64
+#define INTROSPECTION_ENUM_PREFIX AARCH64
+#include "define_introspection_and_hwcaps.inl"
diff --git a/src/impl_aarch64_linux_or_android.c b/src/impl_aarch64_linux_or_android.c
index ef923d9..c0a764c 100644
--- a/src/impl_aarch64_linux_or_android.c
+++ b/src/impl_aarch64_linux_or_android.c
@@ -17,81 +17,7 @@
 #ifdef CPU_FEATURES_ARCH_AARCH64
 #if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
 
-#include "cpuinfo_aarch64.h"
-
-////////////////////////////////////////////////////////////////////////////////
-// Definitions for introspection.
-////////////////////////////////////////////////////////////////////////////////
-#define INTROSPECTION_TABLE                                                \
-  LINE(AARCH64_FP, fp, "fp", AARCH64_HWCAP_FP, 0)                          \
-  LINE(AARCH64_ASIMD, asimd, "asimd", AARCH64_HWCAP_ASIMD, 0)              \
-  LINE(AARCH64_EVTSTRM, evtstrm, "evtstrm", AARCH64_HWCAP_EVTSTRM, 0)      \
-  LINE(AARCH64_AES, aes, "aes", AARCH64_HWCAP_AES, 0)                      \
-  LINE(AARCH64_PMULL, pmull, "pmull", AARCH64_HWCAP_PMULL, 0)              \
-  LINE(AARCH64_SHA1, sha1, "sha1", AARCH64_HWCAP_SHA1, 0)                  \
-  LINE(AARCH64_SHA2, sha2, "sha2", AARCH64_HWCAP_SHA2, 0)                  \
-  LINE(AARCH64_CRC32, crc32, "crc32", AARCH64_HWCAP_CRC32, 0)              \
-  LINE(AARCH64_ATOMICS, atomics, "atomics", AARCH64_HWCAP_ATOMICS, 0)      \
-  LINE(AARCH64_FPHP, fphp, "fphp", AARCH64_HWCAP_FPHP, 0)                  \
-  LINE(AARCH64_ASIMDHP, asimdhp, "asimdhp", AARCH64_HWCAP_ASIMDHP, 0)      \
-  LINE(AARCH64_CPUID, cpuid, "cpuid", AARCH64_HWCAP_CPUID, 0)              \
-  LINE(AARCH64_ASIMDRDM, asimdrdm, "asimdrdm", AARCH64_HWCAP_ASIMDRDM, 0)  \
-  LINE(AARCH64_JSCVT, jscvt, "jscvt", AARCH64_HWCAP_JSCVT, 0)              \
-  LINE(AARCH64_FCMA, fcma, "fcma", AARCH64_HWCAP_FCMA, 0)                  \
-  LINE(AARCH64_LRCPC, lrcpc, "lrcpc", AARCH64_HWCAP_LRCPC, 0)              \
-  LINE(AARCH64_DCPOP, dcpop, "dcpop", AARCH64_HWCAP_DCPOP, 0)              \
-  LINE(AARCH64_SHA3, sha3, "sha3", AARCH64_HWCAP_SHA3, 0)                  \
-  LINE(AARCH64_SM3, sm3, "sm3", AARCH64_HWCAP_SM3, 0)                      \
-  LINE(AARCH64_SM4, sm4, "sm4", AARCH64_HWCAP_SM4, 0)                      \
-  LINE(AARCH64_ASIMDDP, asimddp, "asimddp", AARCH64_HWCAP_ASIMDDP, 0)      \
-  LINE(AARCH64_SHA512, sha512, "sha512", AARCH64_HWCAP_SHA512, 0)          \
-  LINE(AARCH64_SVE, sve, "sve", AARCH64_HWCAP_SVE, 0)                      \
-  LINE(AARCH64_ASIMDFHM, asimdfhm, "asimdfhm", AARCH64_HWCAP_ASIMDFHM, 0)  \
-  LINE(AARCH64_DIT, dit, "dit", AARCH64_HWCAP_DIT, 0)                      \
-  LINE(AARCH64_USCAT, uscat, "uscat", AARCH64_HWCAP_USCAT, 0)              \
-  LINE(AARCH64_ILRCPC, ilrcpc, "ilrcpc", AARCH64_HWCAP_ILRCPC, 0)          \
-  LINE(AARCH64_FLAGM, flagm, "flagm", AARCH64_HWCAP_FLAGM, 0)              \
-  LINE(AARCH64_SSBS, ssbs, "ssbs", AARCH64_HWCAP_SSBS, 0)                  \
-  LINE(AARCH64_SB, sb, "sb", AARCH64_HWCAP_SB, 0)                          \
-  LINE(AARCH64_PACA, paca, "paca", AARCH64_HWCAP_PACA, 0)                  \
-  LINE(AARCH64_PACG, pacg, "pacg", AARCH64_HWCAP_PACG, 0)                  \
-  LINE(AARCH64_DCPODP, dcpodp, "dcpodp", 0, AARCH64_HWCAP2_DCPODP)         \
-  LINE(AARCH64_SVE2, sve2, "sve2", 0, AARCH64_HWCAP2_SVE2)                 \
-  LINE(AARCH64_SVEAES, sveaes, "sveaes", 0, AARCH64_HWCAP2_SVEAES)         \
-  LINE(AARCH64_SVEPMULL, svepmull, "svepmull", 0, AARCH64_HWCAP2_SVEPMULL) \
-  LINE(AARCH64_SVEBITPERM, svebitperm, "svebitperm", 0,                    \
-       AARCH64_HWCAP2_SVEBITPERM)                                          \
-  LINE(AARCH64_SVESHA3, svesha3, "svesha3", 0, AARCH64_HWCAP2_SVESHA3)     \
-  LINE(AARCH64_SVESM4, svesm4, "svesm4", 0, AARCH64_HWCAP2_SVESM4)         \
-  LINE(AARCH64_FLAGM2, flagm2, "flagm2", 0, AARCH64_HWCAP2_FLAGM2)         \
-  LINE(AARCH64_FRINT, frint, "frint", 0, AARCH64_HWCAP2_FRINT)             \
-  LINE(AARCH64_SVEI8MM, svei8mm, "svei8mm", 0, AARCH64_HWCAP2_SVEI8MM)     \
-  LINE(AARCH64_SVEF32MM, svef32mm, "svef32mm", 0, AARCH64_HWCAP2_SVEF32MM) \
-  LINE(AARCH64_SVEF64MM, svef64mm, "svef64mm", 0, AARCH64_HWCAP2_SVEF64MM) \
-  LINE(AARCH64_SVEBF16, svebf16, "svebf16", 0, AARCH64_HWCAP2_SVEBF16)     \
-  LINE(AARCH64_I8MM, i8mm, "i8mm", 0, AARCH64_HWCAP2_I8MM)                 \
-  LINE(AARCH64_BF16, bf16, "bf16", 0, AARCH64_HWCAP2_BF16)                 \
-  LINE(AARCH64_DGH, dgh, "dgh", 0, AARCH64_HWCAP2_DGH)                     \
-  LINE(AARCH64_RNG, rng, "rng", 0, AARCH64_HWCAP2_RNG)                     \
-  LINE(AARCH64_BTI, bti, "bti", 0, AARCH64_HWCAP2_BTI)                     \
-  LINE(AARCH64_MTE, mte, "mte", 0, AARCH64_HWCAP2_MTE)                     \
-  LINE(AARCH64_ECV, ecv, "ecv", 0, AARCH64_HWCAP2_ECV)                     \
-  LINE(AARCH64_AFP, afp, "afp", 0, AARCH64_HWCAP2_AFP)                     \
-  LINE(AARCH64_RPRES, rpres, "rpres", 0, AARCH64_HWCAP2_RPRES)
-#define INTROSPECTION_PREFIX Aarch64
-#define INTROSPECTION_ENUM_PREFIX AARCH64
-#include "define_introspection_and_hwcaps.inl"
-
-////////////////////////////////////////////////////////////////////////////////
-// Implementation.
-////////////////////////////////////////////////////////////////////////////////
-
-#include <stdbool.h>
-
-#include "internal/bit_utils.h"
-#include "internal/filesystem.h"
-#include "internal/stack_line_reader.h"
-#include "internal/string_view.h"
+#include "impl_aarch64__base_implementation.inl"
 
 static bool HandleAarch64Line(const LineResult result,
                               Aarch64Info* const info) {
diff --git a/src/impl_aarch64_macos_or_iphone.c b/src/impl_aarch64_macos_or_iphone.c
new file mode 100644
index 0000000..7b97896
--- /dev/null
+++ b/src/impl_aarch64_macos_or_iphone.c
@@ -0,0 +1,88 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_AARCH64
+#if defined(CPU_FEATURES_OS_MACOS) || defined(CPU_FEATURES_OS_IPHONE)
+
+#include "impl_aarch64__base_implementation.inl"
+
+#if !defined(HAVE_SYSCTLBYNAME)
+#error "Darwin needs support for sysctlbyname"
+#endif
+#include <sys/sysctl.h>
+
+#if defined(CPU_FEATURES_MOCK_SYSCTL_AARCH64)
+extern bool GetDarwinSysCtlByName(const char*);
+extern int GetDarwinSysCtlByNameValue(const char* name);
+#else
+static int GetDarwinSysCtlByNameValue(const char* name) {
+  int enabled;
+  size_t enabled_len = sizeof(enabled);
+  const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0);
+  return failure ? 0 : enabled;
+}
+
+static bool GetDarwinSysCtlByName(const char* name) {
+  return GetDarwinSysCtlByNameValue(name) != 0;
+}
+#endif
+
+static const Aarch64Info kEmptyAarch64Info;
+
+Aarch64Info GetAarch64Info(void) {
+  Aarch64Info info = kEmptyAarch64Info;
+
+  // Handling Darwin platform through sysctlbyname.
+  info.implementer = GetDarwinSysCtlByNameValue("hw.cputype");
+  info.variant = GetDarwinSysCtlByNameValue("hw.cpusubtype");
+  info.part = GetDarwinSysCtlByNameValue("hw.cpufamily");
+  info.revision = GetDarwinSysCtlByNameValue("hw.cpusubfamily");
+
+  info.features.fp = GetDarwinSysCtlByName("hw.optional.floatingpoint");
+  info.features.asimd = GetDarwinSysCtlByName("hw.optional.AdvSIMD");
+  info.features.aes = GetDarwinSysCtlByName("hw.optional.arm.FEAT_AES");
+  info.features.pmull = GetDarwinSysCtlByName("hw.optional.arm.FEAT_PMULL");
+  info.features.sha1 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA1");
+  info.features.sha2 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA256");
+  info.features.crc32 = GetDarwinSysCtlByName("hw.optional.armv8_crc32");
+  info.features.atomics = GetDarwinSysCtlByName("hw.optional.arm.FEAT_LSE");
+  info.features.fphp = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FP16");
+  info.features.asimdhp = GetDarwinSysCtlByName("hw.optional.arm.AdvSIMD_HPFPCvt");
+  info.features.asimdrdm = GetDarwinSysCtlByName("hw.optional.arm.FEAT_RDM");
+  info.features.jscvt = GetDarwinSysCtlByName("hw.optional.arm.FEAT_JSCVT");
+  info.features.fcma = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FCMA");
+  info.features.lrcpc = GetDarwinSysCtlByName("hw.optional.arm.FEAT_LRCPC");
+  info.features.dcpop = GetDarwinSysCtlByName("hw.optional.arm.FEAT_DPB");
+  info.features.sha3 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA3");
+  info.features.asimddp = GetDarwinSysCtlByName("hw.optional.arm.FEAT_DotProd");
+  info.features.sha512 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA512");
+  info.features.asimdfhm = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FHM");
+  info.features.dit = GetDarwinSysCtlByName("hw.optional.arm.FEAT_DIT");
+  info.features.uscat = GetDarwinSysCtlByName("hw.optional.arm.FEAT_LSE2");
+  info.features.flagm = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FlagM");
+  info.features.ssbs = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SSBS");
+  info.features.sb = GetDarwinSysCtlByName("hw.optional.arm.FEAT_SB");
+  info.features.flagm2 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FlagM2");
+  info.features.frint = GetDarwinSysCtlByName("hw.optional.arm.FEAT_FRINTTS");
+  info.features.i8mm = GetDarwinSysCtlByName("hw.optional.arm.FEAT_I8MM");
+  info.features.bf16 = GetDarwinSysCtlByName("hw.optional.arm.FEAT_BF16");
+  info.features.bti = GetDarwinSysCtlByName("hw.optional.arm.FEAT_BTI");
+
+  return info;
+}
+
+#endif  // defined(CPU_FEATURES_OS_MACOS) || defined(CPU_FEATURES_OS_IPHONE)
+#endif  // CPU_FEATURES_ARCH_AARCH64
diff --git a/src/impl_loongarch_linux.c b/src/impl_loongarch_linux.c
new file mode 100644
index 0000000..b9cea7b
--- /dev/null
+++ b/src/impl_loongarch_linux.c
@@ -0,0 +1,89 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_LOONGARCH
+#if defined(CPU_FEATURES_OS_LINUX)
+
+#include "cpuinfo_loongarch.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                            \
+  LINE(LOONGARCH_CPUCFG, CPUCFG, "cfg", HWCAP_LOONGARCH_CPUCFG, 0) \
+  LINE(LOONGARCH_LAM, LAM, "lam", HWCAP_LOONGARCH_LAM, 0) \
+  LINE(LOONGARCH_UAL, UAL, "ual", HWCAP_LOONGARCH_UAL, 0) \
+  LINE(LOONGARCH_FPU, FPU, "fpu", HWCAP_LOONGARCH_FPU, 0) \
+  LINE(LOONGARCH_LSX, LSX, "lsx", HWCAP_LOONGARCH_LSX, 0) \
+  LINE(LOONGARCH_LASX, LASX, "lasx", HWCAP_LOONGARCH_LASX, 0) \
+  LINE(LOONGARCH_CRC32, CRC32, "crc32", HWCAP_LOONGARCH_CRC32, 0) \
+  LINE(LOONGARCH_COMPLEX, COMPLEX, "complex", HWCAP_LOONGARCH_COMPLEX, 0) \
+  LINE(LOONGARCH_CRYPTO, CRYPTO, "crypto", HWCAP_LOONGARCH_CRYPTO, 0) \
+  LINE(LOONGARCH_LVZ, LVZ, "lvz", HWCAP_LOONGARCH_LVZ, 0) \
+  LINE(LOONGARCH_LBT_X86, LBT_X86, "lbt_x86", HWCAP_LOONGARCH_LBT_X86, 0) \
+  LINE(LOONGARCH_LBT_ARM, LBT_ARM, "lbt_arm", HWCAP_LOONGARCH_LBT_ARM, 0) \
+  LINE(LOONGARCH_LBT_MIPS, LBT_MIPS, "lbt_mips", HWCAP_LOONGARCH_LBT_MIPS, 0) \
+  LINE(LOONGARCH_PTW, PTW, "ptw", HWCAP_LOONGARCH_PTW, 0)
+#define INTROSPECTION_PREFIX LoongArch
+#define INTROSPECTION_ENUM_PREFIX LOONGARCH
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+
+static const LoongArchInfo kEmptyLoongArchInfo;
+
+static bool HandleLoongArchLine(const LineResult result, LoongArchInfo* const info) {
+  StringView line = result.line;
+  StringView key, value;
+  if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
+    if (CpuFeatures_StringView_IsEquals(key, str("Features"))) {
+      for (size_t i = 0; i < LOONGARCH_LAST_; ++i) {
+        kSetters[i](&info->features, CpuFeatures_StringView_HasWord(
+                                         value, kCpuInfoFlags[i], ' '));
+      }
+    }
+  }
+  return !result.eof;
+}
+
+static void FillProcCpuInfoData(LoongArchInfo* const info) {
+  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandleLoongArchLine(StackLineReader_NextLine(&reader), info)) break;
+    }
+    CpuFeatures_CloseFile(fd);
+  }
+}
+
+LoongArchInfo GetLoongArchInfo(void) {
+  LoongArchInfo info = kEmptyLoongArchInfo;
+  FillProcCpuInfoData(&info);
+  return info;
+}
+
+#endif  // defined(CPU_FEATURES_OS_LINUX)
+#endif  // CPU_FEATURES_ARCH_LOONGARCH
diff --git a/src/impl_x86__base_implementation.inl b/src/impl_x86__base_implementation.inl
index 6a34bff..dfff46c 100644
--- a/src/impl_x86__base_implementation.inl
+++ b/src/impl_x86__base_implementation.inl
@@ -166,6 +166,7 @@
   Leaf leaf_80000002;  // brand string
   Leaf leaf_80000003;  // brand string
   Leaf leaf_80000004;  // brand string
+  Leaf leaf_80000021;  // AMD Extended Feature Identification 2
 } Leaves;
 
 static Leaves ReadLeaves(void) {
@@ -186,6 +187,7 @@
       .leaf_80000002 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000002, 0),
       .leaf_80000003 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000003, 0),
       .leaf_80000004 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000004, 0),
+      .leaf_80000021 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000021, 0),
   };
 }
 
@@ -390,6 +392,7 @@
   features->fs_rep_cmpsb_scasb = IsBitSet(leaf_7_1.eax, 12);
   features->adx = IsBitSet(leaf_7.ebx, 19);
   features->lzcnt = IsBitSet(leaf_80000001.ecx, 5);
+  features->lam = IsBitSet(leaf_7_1.eax, 26);
 
   /////////////////////////////////////////////////////////////////////////////
   // The following section is devoted to Vector Extensions.
@@ -447,6 +450,7 @@
       features->amx_bf16 = IsBitSet(leaf_7.edx, 22);
       features->amx_tile = IsBitSet(leaf_7.edx, 24);
       features->amx_int8 = IsBitSet(leaf_7.edx, 25);
+      features->amx_fp16 = IsBitSet(leaf_7_1.eax, 21);
     }
   } else {
     // When XCR0 is not available (Atom based or older cpus) we need to defer to
@@ -462,6 +466,7 @@
 static void ParseExtraAMDCpuId(const Leaves* leaves, X86Info* info,
                                OsPreserves os_preserves) {
   const Leaf leaf_80000001 = leaves->leaf_80000001;
+  const Leaf leaf_80000021 = leaves->leaf_80000021;
 
   X86Features* const features = &info->features;
 
@@ -472,6 +477,8 @@
   if (os_preserves.avx_registers) {
     features->fma4 = IsBitSet(leaf_80000001.ecx, 16);
   }
+
+  features->uai = IsBitSet(leaf_80000021.eax, 7);
 }
 
 static const X86Info kEmptyX86Info;
@@ -641,6 +648,7 @@
         }
       case CPUID(0x06, 0x97):
       case CPUID(0x06, 0x9A):
+      case CPUID(0x06, 0xBE):
         // https://en.wikichip.org/wiki/intel/microarchitectures/alder_lake
         return INTEL_ADL;
       case CPUID(0x06, 0xA5):
@@ -811,6 +819,7 @@
       case CPUID(0x17, 0x60):
       case CPUID(0x17, 0x68):
       case CPUID(0x17, 0x71):
+      case CPUID(0x17, 0x84):
       case CPUID(0x17, 0x90):
       case CPUID(0x17, 0x98):
       case CPUID(0x17, 0xA0):
@@ -827,7 +836,9 @@
         // https://en.wikichip.org/wiki/amd/microarchitectures/zen_3
         return AMD_ZEN3;
       case CPUID(0x19, 0x10):
+      case CPUID(0x19, 0x11):
       case CPUID(0x19, 0x61):
+      case CPUID(0x19, 0x74):
         // https://en.wikichip.org/wiki/amd/microarchitectures/zen_4
         return AMD_ZEN4;
       default:
@@ -1966,6 +1977,7 @@
   LINE(X86_AMX_BF16, amx_bf16, , , )                       \
   LINE(X86_AMX_TILE, amx_tile, , , )                       \
   LINE(X86_AMX_INT8, amx_int8, , , )                       \
+  LINE(X86_AMX_FP16, amx_fp16, , , )                       \
   LINE(X86_PCLMULQDQ, pclmulqdq, , , )                     \
   LINE(X86_SMX, smx, , , )                                 \
   LINE(X86_SGX, sgx, , , )                                 \
@@ -1984,7 +1996,9 @@
   LINE(X86_FS_REP_MOV, fs_rep_mov, , , )                   \
   LINE(X86_FZ_REP_MOVSB, fz_rep_movsb, , , )               \
   LINE(X86_FS_REP_STOSB, fs_rep_stosb, , , )               \
-  LINE(X86_FS_REP_CMPSB_SCASB, fs_rep_cmpsb_scasb, , , )
+  LINE(X86_FS_REP_CMPSB_SCASB, fs_rep_cmpsb_scasb, , , )   \
+  LINE(X86_LAM, lam, , , )                                 \
+  LINE(X86_UAI, uai, , , )
 #define INTROSPECTION_PREFIX X86
 #define INTROSPECTION_ENUM_PREFIX X86
 #include "define_introspection.inl"
diff --git a/src/utils/list_cpu_features.c b/src/utils/list_cpu_features.c
index 8226d85..83cd387 100644
--- a/src/utils/list_cpu_features.c
+++ b/src/utils/list_cpu_features.c
@@ -39,6 +39,8 @@
 #include "cpuinfo_s390x.h"
 #elif defined(CPU_FEATURES_ARCH_RISCV)
 #include "cpuinfo_riscv.h"
+#elif defined(CPU_FEATURES_ARCH_LOONGARCH)
+#include "cpuinfo_loongarch.h"
 #endif
 
 // Design principles
@@ -215,6 +217,9 @@
 #elif defined(CPU_FEATURES_ARCH_RISCV)
 DEFINE_ADD_FLAGS(GetRiscvFeaturesEnumValue, GetRiscvFeaturesEnumName, RiscvFeatures,
                  RISCV_LAST_)
+#elif defined(CPU_FEATURES_ARCH_LOONGARCH)
+DEFINE_ADD_FLAGS(GetLoongArchFeaturesEnumValue, GetLoongArchFeaturesEnumName, LoongArchFeatures,
+                 LOONGARCH_LAST_)
 #endif
 
 // Prints a json string with characters escaping.
@@ -432,6 +437,10 @@
   AddMapEntry(root, "vendor", CreateString(info.vendor));
   AddMapEntry(root, "microarchitecture", CreateString(info.uarch));
   AddFlags(root, &info.features); 
+#elif defined(CPU_FEATURES_ARCH_LOONGARCH)
+  const LoongArchInfo info = GetLoongArchInfo();
+  AddMapEntry(root, "arch", CreateString("loongarch"));
+  AddFlags(root, &info.features); 
 #endif
   return root;
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f627d74..f21ff98 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -74,8 +74,15 @@
   add_executable(cpuinfo_aarch64_test
     cpuinfo_aarch64_test.cc
     ../src/impl_aarch64_linux_or_android.c
-    ../src/impl_aarch64_windows.c)
-  target_compile_definitions(cpuinfo_aarch64_test PUBLIC CPU_FEATURES_MOCK_CPUID_AARCH64)
+    ../src/impl_aarch64_windows.c
+    ../src/impl_aarch64_macos_or_iphone.c
+  )
+  if(APPLE)
+    target_compile_definitions(cpuinfo_aarch64_test PUBLIC CPU_FEATURES_MOCK_SYSCTL_AARCH64)
+    target_compile_definitions(cpuinfo_aarch64_test PRIVATE HAVE_SYSCTLBYNAME)
+  else()
+    target_compile_definitions(cpuinfo_aarch64_test PUBLIC CPU_FEATURES_MOCK_CPUID_AARCH64)
+  endif()
   target_link_libraries(cpuinfo_aarch64_test all_libraries)
   add_test(NAME cpuinfo_aarch64_test COMMAND cpuinfo_aarch64_test)
 endif()
@@ -107,3 +114,11 @@
   target_link_libraries(cpuinfo_riscv_test all_libraries)
   add_test(NAME cpuinfo_riscv_test COMMAND cpuinfo_riscv_test)
 endif()
+##------------------------------------------------------------------------------
+## cpuinfo_loongarch_test
+if(PROCESSOR_IS_LOONGARCH)
+  add_executable(cpuinfo_loongarch_test cpuinfo_loongarch_test.cc  ../src/impl_loongarch_linux.c)
+  target_link_libraries(cpuinfo_loongarch_test all_libraries)
+  add_test(NAME cpuinfo_loongarch_test COMMAND cpuinfo_loongarch_test)
+endif()
+
diff --git a/test/cpuinfo_aarch64_test.cc b/test/cpuinfo_aarch64_test.cc
index ef9abae..6aefa6b 100644
--- a/test/cpuinfo_aarch64_test.cc
+++ b/test/cpuinfo_aarch64_test.cc
@@ -25,8 +25,35 @@
 
 namespace cpu_features {
 class FakeCpuAarch64 {
+#if defined(CPU_FEATURES_OS_LINUX)
+  // No particular implementation for Linux as we use /proc/cpuinfo
+#elif defined(CPU_FEATURES_OS_MACOS)
+  std::set<std::string> darwin_sysctlbyname_;
+  std::map<std::string, int> darwin_sysctlbynamevalue_;
+
  public:
-#if defined(CPU_FEATURES_OS_WINDOWS)
+  bool GetDarwinSysCtlByName(std::string name) const {
+    return darwin_sysctlbyname_.count(name);
+  }
+
+  int GetDarwinSysCtlByNameValue(std::string name) const {
+    const auto iter = darwin_sysctlbynamevalue_.find(name);
+    if (iter != darwin_sysctlbynamevalue_.end()) return iter->second;
+    return 0;
+  }
+
+  void SetDarwinSysCtlByName(std::string name) {
+    darwin_sysctlbyname_.insert(name);
+  }
+
+  void SetDarwinSysCtlByNameValue(std::string name, int value) {
+    darwin_sysctlbynamevalue_[name] = value;
+  }
+#elif defined(CPU_FEATURES_OS_WINDOWS)
+  std::set<DWORD> windows_isprocessorfeaturepresent_;
+  WORD processor_revision_{};
+
+ public:
   bool GetWindowsIsProcessorFeaturePresent(DWORD dwProcessorFeature) {
     return windows_isprocessorfeaturepresent_.count(dwProcessorFeature);
   }
@@ -42,11 +69,7 @@
   void SetWindowsNativeSystemInfoProcessorRevision(WORD wProcessorRevision) {
     processor_revision_ = wProcessorRevision;
   }
-
- private:
-  std::set<DWORD> windows_isprocessorfeaturepresent_;
-  WORD processor_revision_{};
-#endif  // CPU_FEATURES_OS_WINDOWS
+#endif
 };
 
 static FakeCpuAarch64* g_fake_cpu_instance = nullptr;
@@ -56,7 +79,18 @@
   return *g_fake_cpu_instance;
 }
 
-#if defined(CPU_FEATURES_OS_WINDOWS)
+// Define OS dependent mock functions
+#if defined(CPU_FEATURES_OS_LINUX)
+// No particular functions to implement for Linux as we use /proc/cpuinfo
+#elif defined(CPU_FEATURES_OS_MACOS)
+extern "C" bool GetDarwinSysCtlByName(const char* name) {
+  return cpu().GetDarwinSysCtlByName(name);
+}
+
+extern "C" int GetDarwinSysCtlByNameValue(const char* name) {
+  return cpu().GetDarwinSysCtlByNameValue(name);
+}
+#elif defined(CPU_FEATURES_OS_WINDOWS)
 extern "C" bool GetWindowsIsProcessorFeaturePresent(DWORD dwProcessorFeature) {
   return cpu().GetWindowsIsProcessorFeaturePresent(dwProcessorFeature);
 }
@@ -64,7 +98,7 @@
 extern "C" WORD GetWindowsNativeSystemInfoProcessorRevision() {
   return cpu().GetWindowsNativeSystemInfoProcessorRevision();
 }
-#endif  // CPU_FEATURES_OS_WINDOWS
+#endif
 
 namespace {
 
@@ -80,7 +114,7 @@
   }
 };
 
-TEST(CpuinfoAarch64Test, Aarch64FeaturesEnum) {
+TEST_F(CpuidAarch64Test, Aarch64FeaturesEnum) {
   const char* last_name = GetAarch64FeaturesEnumName(AARCH64_LAST_);
   EXPECT_STREQ(last_name, "unknown_feature");
   for (int i = static_cast<int>(AARCH64_FP);
@@ -93,10 +127,9 @@
   }
 }
 
+// OS dependent tests
 #if defined(CPU_FEATURES_OS_LINUX)
-void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
-
-TEST(CpuinfoAarch64Test, FromHardwareCap) {
+TEST_F(CpuidAarch64Test, FromHardwareCap) {
   ResetHwcaps();
   SetHardwareCapabilities(AARCH64_HWCAP_FP | AARCH64_HWCAP_AES, 0);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
@@ -135,7 +168,7 @@
   EXPECT_FALSE(info.features.pacg);
 }
 
-TEST(CpuinfoAarch64Test, FromHardwareCap2) {
+TEST_F(CpuidAarch64Test, FromHardwareCap2) {
   ResetHwcaps();
   SetHardwareCapabilities(AARCH64_HWCAP_FP,
                           AARCH64_HWCAP2_SVE2 | AARCH64_HWCAP2_BTI);
@@ -164,7 +197,7 @@
   EXPECT_FALSE(info.features.rng);
 }
 
-TEST(CpuinfoAarch64Test, ARMCortexA53) {
+TEST_F(CpuidAarch64Test, ARMCortexA53) {
   ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
@@ -244,10 +277,90 @@
   EXPECT_FALSE(info.features.ecv);
   EXPECT_FALSE(info.features.afp);
   EXPECT_FALSE(info.features.rpres);
+  EXPECT_FALSE(info.features.mte3);
+  EXPECT_FALSE(info.features.sme);
+  EXPECT_FALSE(info.features.smei16i64);
+  EXPECT_FALSE(info.features.smef64f64);
+  EXPECT_FALSE(info.features.smei8i32);
+  EXPECT_FALSE(info.features.smef16f32);
+  EXPECT_FALSE(info.features.smeb16f32);
+  EXPECT_FALSE(info.features.smef32f32);
+  EXPECT_FALSE(info.features.smefa64);
+  EXPECT_FALSE(info.features.wfxt);
+  EXPECT_FALSE(info.features.ebf16);
+  EXPECT_FALSE(info.features.sveebf16);
+  EXPECT_FALSE(info.features.cssc);
+  EXPECT_FALSE(info.features.rprfm);
+  EXPECT_FALSE(info.features.sve2p1);
+  EXPECT_FALSE(info.features.sme2);
+  EXPECT_FALSE(info.features.sme2p1);
+  EXPECT_FALSE(info.features.smei16i32);
+  EXPECT_FALSE(info.features.smebi32i32);
+  EXPECT_FALSE(info.features.smeb16b16);
+  EXPECT_FALSE(info.features.smef16f16);
 }
-#endif  // CPU_FEATURES_OS_LINUX
+#elif defined(CPU_FEATURES_OS_MACOS)
+TEST_F(CpuidAarch64Test, FromDarwinSysctlFromName) {
+  cpu().SetDarwinSysCtlByName("hw.optional.floatingpoint");
+  cpu().SetDarwinSysCtlByName("hw.optional.neon");
+  cpu().SetDarwinSysCtlByName("hw.optional.AdvSIMD_HPFPCvt");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm.FEAT_FP16");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm.FEAT_LSE");
+  cpu().SetDarwinSysCtlByName("hw.optional.armv8_crc32");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm.FEAT_FHM");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA512");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm.FEAT_SHA3");
+  cpu().SetDarwinSysCtlByName("hw.optional.amx_version");
+  cpu().SetDarwinSysCtlByName("hw.optional.ucnormal_mem");
+  cpu().SetDarwinSysCtlByName("hw.optional.arm64");
 
-#if defined(CPU_FEATURES_OS_WINDOWS)
+  cpu().SetDarwinSysCtlByNameValue("hw.cputype", 16777228);
+  cpu().SetDarwinSysCtlByNameValue("hw.cpusubtype", 2);
+  cpu().SetDarwinSysCtlByNameValue("hw.cpu64bit", 1);
+  cpu().SetDarwinSysCtlByNameValue("hw.cpufamily", 458787763);
+  cpu().SetDarwinSysCtlByNameValue("hw.cpusubfamily", 2);
+
+  const auto info = GetAarch64Info();
+
+  EXPECT_EQ(info.implementer, 0x100000C);
+  EXPECT_EQ(info.variant, 2);
+  EXPECT_EQ(info.part, 0x1B588BB3);
+  EXPECT_EQ(info.revision, 2);
+
+  EXPECT_TRUE(info.features.fp);
+  EXPECT_FALSE(info.features.asimd);
+  EXPECT_FALSE(info.features.evtstrm);
+  EXPECT_FALSE(info.features.aes);
+  EXPECT_FALSE(info.features.pmull);
+  EXPECT_FALSE(info.features.sha1);
+  EXPECT_FALSE(info.features.sha2);
+  EXPECT_TRUE(info.features.crc32);
+  EXPECT_TRUE(info.features.atomics);
+  EXPECT_TRUE(info.features.fphp);
+  EXPECT_FALSE(info.features.asimdhp);
+  EXPECT_FALSE(info.features.cpuid);
+  EXPECT_FALSE(info.features.asimdrdm);
+  EXPECT_FALSE(info.features.jscvt);
+  EXPECT_FALSE(info.features.fcma);
+  EXPECT_FALSE(info.features.lrcpc);
+  EXPECT_FALSE(info.features.dcpop);
+  EXPECT_TRUE(info.features.sha3);
+  EXPECT_FALSE(info.features.sm3);
+  EXPECT_FALSE(info.features.sm4);
+  EXPECT_FALSE(info.features.asimddp);
+  EXPECT_TRUE(info.features.sha512);
+  EXPECT_FALSE(info.features.sve);
+  EXPECT_TRUE(info.features.asimdfhm);
+  EXPECT_FALSE(info.features.dit);
+  EXPECT_FALSE(info.features.uscat);
+  EXPECT_FALSE(info.features.ilrcpc);
+  EXPECT_FALSE(info.features.flagm);
+  EXPECT_FALSE(info.features.ssbs);
+  EXPECT_FALSE(info.features.sb);
+  EXPECT_FALSE(info.features.paca);
+  EXPECT_FALSE(info.features.pacg);
+}
+#elif defined(CPU_FEATURES_OS_WINDOWS)
 TEST_F(CpuidAarch64Test, WINDOWS_AARCH64_RPI4) {
   cpu().SetWindowsNativeSystemInfoProcessorRevision(0x03);
   cpu().SetWindowsIsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE);
@@ -271,6 +384,5 @@
   EXPECT_FALSE(info.features.lrcpc);
 }
 #endif  // CPU_FEATURES_OS_WINDOWS
-
 }  // namespace
 }  // namespace cpu_features
diff --git a/test/cpuinfo_loongarch_test.cc b/test/cpuinfo_loongarch_test.cc
new file mode 100644
index 0000000..14f544f
--- /dev/null
+++ b/test/cpuinfo_loongarch_test.cc
@@ -0,0 +1,179 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpuinfo_loongarch.h"
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+
+namespace cpu_features {
+namespace {
+
+TEST(CpuinfoLoongArchvTest, UnknownFromCpuInfo) {
+  ResetHwcaps();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo", R"(
+system type		: generic-loongson-machine
+
+processor		: 0
+package			: 0
+core			: 0
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000-HV
+CPU Revision		: 0x11
+FPU Revision		: 0x00
+CPU MHz			: 2500.00
+BogoMIPS		: 5000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips
+Hardware Watchpoint	: yes, iwatch count: 8, dwatch count: 8
+
+processor		: 1
+package			: 0
+core			: 1
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000-HV
+CPU Revision		: 0x11
+FPU Revision		: 0x00
+CPU MHz			: 2500.00
+BogoMIPS		: 5000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips
+Hardware Watchpoint	: yes, iwatch count: 8, dwatch count: 8
+
+processor		: 2
+package			: 0
+core			: 2
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000-HV
+CPU Revision		: 0x11
+FPU Revision		: 0x00
+CPU MHz			: 2500.00
+BogoMIPS		: 5000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips
+Hardware Watchpoint	: yes, iwatch count: 8, dwatch count: 8
+
+processor		: 3
+package			: 0
+core			: 3
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000-HV
+CPU Revision		: 0x11
+FPU Revision		: 0x00
+CPU MHz			: 2500.00
+BogoMIPS		: 5000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu lsx lasx crc32 complex crypto lvz lbt_x86 lbt_arm lbt_mips
+Hardware Watchpoint	: yes, iwatch count: 8, dwatch count: 8)");
+  const auto info = GetLoongArchInfo();
+  EXPECT_FALSE(info.features.CPUCFG);
+  EXPECT_TRUE(info.features.LAM);
+  EXPECT_TRUE(info.features.UAL);
+  EXPECT_TRUE(info.features.FPU);
+  EXPECT_TRUE(info.features.LSX);
+  EXPECT_TRUE(info.features.LASX);
+  EXPECT_TRUE(info.features.CRC32);
+  EXPECT_TRUE(info.features.COMPLEX);
+  EXPECT_TRUE(info.features.CRYPTO);
+  EXPECT_TRUE(info.features.LVZ);
+  EXPECT_TRUE(info.features.LBT_X86);
+  EXPECT_TRUE(info.features.LBT_ARM);
+  EXPECT_TRUE(info.features.LBT_MIPS);
+}
+
+TEST(CpuinfoLoongArchvTest, QemuCpuInfo) {
+  ResetHwcaps();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo", R"(
+system type		: generic-loongson-machine
+
+processor		: 0
+package			: 0
+core			: 0
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000
+CPU Revision		: 0x10
+FPU Revision		: 0x01
+CPU MHz			: 2000.00
+BogoMIPS		: 4000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu crc32
+Hardware Watchpoint	: yes, iwatch count: 0, dwatch count: 0
+
+processor		: 1
+package			: 0
+core			: 1
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000
+CPU Revision		: 0x10
+FPU Revision		: 0x01
+CPU MHz			: 2000.00
+BogoMIPS		: 4000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu crc32
+Hardware Watchpoint	: yes, iwatch count: 0, dwatch count: 0
+
+processor		: 2
+package			: 0
+core			: 2
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000
+CPU Revision		: 0x10
+FPU Revision		: 0x01
+CPU MHz			: 2000.00
+BogoMIPS		: 4000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu crc32
+Hardware Watchpoint	: yes, iwatch count: 0, dwatch count: 0
+
+processor		: 3
+package			: 0
+core			: 3
+CPU Family		: Loongson-64bit
+Model Name		: Loongson-3A5000
+CPU Revision		: 0x10
+FPU Revision		: 0x01
+CPU MHz			: 2000.00
+BogoMIPS		: 4000.00
+TLB Entries		: 2112
+Address Sizes		: 48 bits physical, 48 bits virtual
+ISA			: loongarch32 loongarch64
+Features		: cpucfg lam ual fpu crc32
+Hardware Watchpoint	: yes, iwatch count: 0, dwatch count: 0)");
+  const auto info = GetLoongArchInfo();
+  EXPECT_FALSE(info.features.CPUCFG);
+  EXPECT_TRUE(info.features.LAM);
+  EXPECT_TRUE(info.features.UAL);
+  EXPECT_TRUE(info.features.FPU);
+  EXPECT_TRUE(info.features.CRC32);
+}
+
+}  // namespace
+}  // namespace cpu_features
diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc
index de271cc..328aa5c 100644
--- a/test/cpuinfo_x86_test.cc
+++ b/test/cpuinfo_x86_test.cc
@@ -184,6 +184,8 @@
   EXPECT_FALSE(features.movbe);
   EXPECT_FALSE(features.rdrnd);
   EXPECT_FALSE(features.adx);
+  EXPECT_FALSE(features.lam);
+  EXPECT_FALSE(features.uai);
 }
 
 const int UNDEF = -1;
@@ -770,6 +772,20 @@
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN2);
 }
 
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0880F40_K17_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K17_ZEN2_4800S) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00880F40, 0x00100800, 0x7ED8320B, 0x178BFBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, CPU_FEATURES_VENDOR_AUTHENTIC_AMD);
+  EXPECT_EQ(info.family, 0x17);
+  EXPECT_EQ(info.model, 0x84);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN2);
+}
+
 // http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F02_Hygon_CPUID3.txt
 TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA) {
   cpu().SetLeaves({
@@ -894,6 +910,7 @@
       {{0x80000002, 0}, Leaf{0x20444D41, 0x657A7952, 0x2035206E, 0x30303637}},
       {{0x80000003, 0}, Leaf{0x2D362058, 0x65726F43, 0x6F725020, 0x73736563}},
       {{0x80000004, 0}, Leaf{0x2020726F, 0x20202020, 0x20202020, 0x00202020}},
+      {{0x80000021, 0}, Leaf{0x00062FCF, 0x0000015C, 0x00000000, 0x00000000}},
   });
   const auto info = GetX86Info();
 
@@ -902,9 +919,25 @@
   EXPECT_EQ(info.model, 0x61);
   EXPECT_STREQ(info.brand_string,
                "AMD Ryzen 5 7600X 6-Core Processor             ");
+  EXPECT_TRUE(info.features.uai);
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN4);
 }
 
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0A70F41_K19_Phoenix_03_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K19_ZEN4_PHOENIX) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00A70F41, 0x00100800, 0x7EF8320B, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x80000028, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00A70F41, 0x50000000, 0x75C237FF, 0x2FD3FBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, CPU_FEATURES_VENDOR_AUTHENTIC_AMD);
+  EXPECT_EQ(info.family, 0x19);
+  EXPECT_EQ(info.model, 0x74);
+}
+
 // http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F11_Hygon_01_CPUID.txt
 TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA_OCTAL_CORE_C86_3250) {
   cpu().SetLeaves({
@@ -940,6 +973,20 @@
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN2);
 }
 
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0A10F11_K19_Genoa_02_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K19_ZEN4_GENOA) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00A10F11, 0x00200800, 0x7EFA320B, 0x178BFBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_EQ(info.model, 0x11);
+  EXPECT_EQ(info.family, 0x19);
+  EXPECT_STREQ(info.vendor, CPU_FEATURES_VENDOR_AUTHENTIC_AMD);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN4);
+}
+
 // http://users.atw.hu/instlatx64/GenuineIntel/GenuineIntel00906A4_AlderLakeP_00_CPUID.txt
 TEST_F(CpuidX86Test, INTEL_ALDER_LAKE_AVX_VNNI) {
   cpu().SetOsBackupsExtendedRegisters(true);
@@ -1689,6 +1736,20 @@
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_RPL);
 }
 
+// http://users.atw.hu/instlatx64/GenuineIntel/GenuineIntel00B06E0_AlderLakeN_03_CPUID.txt
+TEST_F(CpuidX86Test, INTEL_ALDER_LAKE_N) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000020, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x000B06E0, 0x00800800, 0x7FFAFBBF, 0xBFEBFBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, CPU_FEATURES_VENDOR_GENUINE_INTEL);
+  EXPECT_EQ(info.family, 0x06);
+  EXPECT_EQ(info.model, 0xBE);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_ADL);
+}
+
 // https://github.com/google/cpu_features/issues/200
 // http://users.atw.hu/instlatx64/GenuineIntel/GenuineIntel00206F2_Eagleton_CPUID.txt
 #if defined(CPU_FEATURES_OS_WINDOWS)