Merge remote-tracking branch 'aosp/upstream-master' into rebase_xnnpack am: 1b64d9d304 am: 782bdb7c29 am: 66e5f4ceed am: 936f298fa0 am: 70f8adc3a7

Original change: https://android-review.googlesource.com/c/platform/external/cpuinfo/+/2203158

Change-Id: Iab9a17491309988a362098d3b54bae7389dc35dd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..583cbbd
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1 @@
+build --symlink_prefix=/ # Out of source build
diff --git a/.bazelversion b/.bazelversion
new file mode 100644
index 0000000..0062ac9
--- /dev/null
+++ b/.bazelversion
@@ -0,0 +1 @@
+5.0.0
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..794e283
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,68 @@
+name: Build using CMake
+on:
+  pull_request:
+  push:
+    branches:
+      - master
+      - main
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
+  cancel-in-progress: true
+jobs:
+  cmake-linux-local:
+    runs-on: ubuntu-latest
+    timeout-minutes: 40
+    steps:
+      - uses: actions/checkout@v2
+      - name: Update apt
+        run: sudo apt update
+      - name: Install ninja
+        run: sudo apt install ninja-build
+      - name: Configure and build
+        run: scripts/local-build.sh
+        working-directory: ${{ github.workspace }}
+  cmake-darwin:
+    runs-on: macos-latest
+    timeout-minutes: 40
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install ninja
+        run: brew install ninja
+      - name: Configure and build
+        run: scripts/local-build.sh
+        working-directory: ${{ github.workspace }}
+  cmake-windows:
+    runs-on: windows-latest
+    timeout-minutes: 40
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install ninja
+        run: choco install ninja
+      - name: Configure and build
+        run: scripts/local-build.sh
+        shell: bash  # Specify bash so we can reuse the build script on Windows (runs on Git bash)
+        working-directory: ${{ github.workspace }}
+  cmake-android:
+    strategy:
+      matrix:
+        script: [android-arm64-build.sh, android-armv7-build.sh, android-x86-build.sh]
+    runs-on: ubuntu-latest
+    timeout-minutes: 40
+    steps:
+      - uses: actions/checkout@v2
+      - name: Update apt
+        run: sudo apt update
+      - name: Install ninja
+        run: sudo apt install ninja-build
+      - name: Setup Android NDK
+        id: setup-ndk
+        uses: nttld/setup-ndk@v1.0.6
+        with:
+          ndk-version: r23b
+          add-to-path: false
+      - name: Configure and build
+        run: scripts/${{ matrix.script }}
+        working-directory: ${{ github.workspace }}
+        env:
+          ANDROID_NDK: ${{ steps.setup-ndk.outputs.ndk-path }}
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..90bef8d
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,416 @@
+# Copied from TensorFlow's `tensorflow/tree/master/third_party/cpuinfo/BUILD
+# Licenced under Apache-2.0 License
+
+# cpuinfo, a library to detect information about the host CPU
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+exports_files(["LICENSE"])
+
+C99OPTS = [
+    "-std=gnu99",  # gnu99, not c99, because dprintf is used
+    "-Wno-vla",
+    "-D_GNU_SOURCE=1",  # to use CPU_SETSIZE
+    "-DCPUINFO_INTERNAL=",
+    "-DCPUINFO_PRIVATE=",
+]
+
+# Source code common to all platforms.
+COMMON_SRCS = [
+    "src/api.c",
+    "src/init.c",
+    "src/cache.c",
+]
+
+# Architecture-specific sources and headers.
+X86_SRCS = [
+    "src/x86/cache/descriptor.c",
+    "src/x86/cache/deterministic.c",
+    "src/x86/cache/init.c",
+    "src/x86/info.c",
+    "src/x86/init.c",
+    "src/x86/isa.c",
+    "src/x86/name.c",
+    "src/x86/topology.c",
+    "src/x86/uarch.c",
+    "src/x86/vendor.c",
+]
+
+ARM_SRCS = [
+    "src/arm/cache.c",
+    "src/arm/uarch.c",
+]
+
+# Platform-specific sources and headers
+LINUX_SRCS = [
+    "src/linux/cpulist.c",
+    "src/linux/multiline.c",
+    "src/linux/processors.c",
+    "src/linux/smallfile.c",
+]
+
+MOCK_LINUX_SRCS = [
+    "src/linux/mockfile.c",
+]
+
+MACH_SRCS = [
+    "src/mach/topology.c",
+]
+
+EMSCRIPTEN_SRCS = [
+    "src/emscripten/init.c",
+]
+
+LINUX_X86_SRCS = [
+    "src/x86/linux/cpuinfo.c",
+    "src/x86/linux/init.c",
+]
+
+LINUX_ARM_SRCS = [
+    "src/arm/linux/chipset.c",
+    "src/arm/linux/clusters.c",
+    "src/arm/linux/cpuinfo.c",
+    "src/arm/linux/hwcap.c",
+    "src/arm/linux/init.c",
+    "src/arm/linux/midr.c",
+]
+
+LINUX_ARM32_SRCS = LINUX_ARM_SRCS + ["src/arm/linux/aarch32-isa.c"]
+
+LINUX_ARM64_SRCS = LINUX_ARM_SRCS + ["src/arm/linux/aarch64-isa.c"]
+
+ANDROID_ARM_SRCS = [
+    "src/arm/android/properties.c",
+]
+
+WINDOWS_X86_SRCS = [
+    "src/x86/windows/init.c",
+]
+
+MACH_X86_SRCS = [
+    "src/x86/mach/init.c",
+]
+
+MACH_ARM_SRCS = [
+    "src/arm/mach/init.c",
+]
+
+EMSCRIPTEN_SRCS = [
+    "src/emscripten/init.c",
+]
+
+cc_library(
+    name = "cpuinfo_impl",
+    srcs = select({
+        ":linux_x86_64": COMMON_SRCS + X86_SRCS + LINUX_SRCS + LINUX_X86_SRCS,
+        ":linux_arm": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM32_SRCS,
+        ":linux_armhf": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM32_SRCS,
+        ":linux_armv7a": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM32_SRCS,
+        ":linux_armeabi": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM32_SRCS,
+        ":linux_aarch64": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM64_SRCS,
+        ":linux_mips64": COMMON_SRCS + LINUX_SRCS,
+        ":linux_riscv64": COMMON_SRCS + LINUX_SRCS,
+        ":linux_s390x": COMMON_SRCS + LINUX_SRCS,
+        ":macos_x86_64": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":macos_arm64": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":windows_x86_64": COMMON_SRCS + X86_SRCS + WINDOWS_X86_SRCS,
+        ":android_armv7": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM32_SRCS + ANDROID_ARM_SRCS,
+        ":android_arm64": COMMON_SRCS + ARM_SRCS + LINUX_SRCS + LINUX_ARM64_SRCS + ANDROID_ARM_SRCS,
+        ":android_x86": COMMON_SRCS + X86_SRCS + LINUX_SRCS + LINUX_X86_SRCS,
+        ":android_x86_64": COMMON_SRCS + X86_SRCS + LINUX_SRCS + LINUX_X86_SRCS,
+        ":ios_x86_64": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":ios_x86": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":ios_armv7": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":ios_arm64": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":ios_arm64e": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":ios_sim_arm64": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":watchos_x86_64": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":watchos_x86": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":watchos_armv7k": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":watchos_arm64_32": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":tvos_x86_64": COMMON_SRCS + X86_SRCS + MACH_SRCS + MACH_X86_SRCS,
+        ":tvos_arm64": COMMON_SRCS + MACH_SRCS + MACH_ARM_SRCS,
+        ":emscripten_wasm": COMMON_SRCS + EMSCRIPTEN_SRCS,
+    }),
+    copts = select({
+        ":windows_x86_64": [],
+        "//conditions:default": C99OPTS,
+    }) + [
+        "-Iexternal/cpuinfo/include",
+        "-Iexternal/cpuinfo/src",
+    ],
+    includes = [
+        "include",
+        "src",
+    ],
+    linkstatic = select({
+        # https://github.com/bazelbuild/bazel/issues/11552
+        ":macos_x86_64": False,
+        "//conditions:default": True,
+    }),
+    # Headers must be in textual_hdrs to allow us to set the standard to C99
+    textual_hdrs = [
+        "include/cpuinfo.h",
+        "src/linux/api.h",
+        "src/mach/api.h",
+        "src/cpuinfo/common.h",
+        "src/cpuinfo/internal-api.h",
+        "src/cpuinfo/log.h",
+        "src/cpuinfo/utils.h",
+        "src/x86/api.h",
+        "src/x86/cpuid.h",
+        "src/x86/linux/api.h",
+        "src/arm/android/api.h",
+        "src/arm/linux/api.h",
+        "src/arm/linux/cp.h",
+        "src/arm/api.h",
+        "src/arm/midr.h",
+    ],
+    deps = [
+        "@org_pytorch_cpuinfo//deps/clog",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo",
+    hdrs = [
+        "include/cpuinfo.h",
+    ],
+    strip_include_prefix = "include",
+    deps = [
+        ":cpuinfo_impl",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo_with_unstripped_include_path",
+    hdrs = [
+        "include/cpuinfo.h",
+    ],
+    deps = [
+        ":cpuinfo_impl",
+    ],
+)
+
+############################# Build configurations #############################
+
+config_setting(
+    name = "linux_x86_64",
+    values = {"cpu": "k8"},
+)
+
+config_setting(
+    name = "linux_arm",
+    values = {"cpu": "arm"},
+)
+
+config_setting(
+    name = "linux_armhf",
+    values = {"cpu": "armhf"},
+)
+
+config_setting(
+    name = "linux_armv7a",
+    values = {"cpu": "armv7a"},
+)
+
+config_setting(
+    name = "linux_armeabi",
+    values = {"cpu": "armeabi"},
+)
+
+config_setting(
+    name = "linux_aarch64",
+    values = {"cpu": "aarch64"},
+)
+
+config_setting(
+    name = "linux_mips64",
+    values = {"cpu": "mips64"},
+)
+
+config_setting(
+    name = "linux_riscv64",
+    values = {"cpu": "riscv64"},
+)
+
+config_setting(
+    name = "linux_s390x",
+    values = {"cpu": "s390x"},
+)
+
+config_setting(
+    name = "macos_x86_64",
+    values = {
+        "apple_platform_type": "macos",
+        "cpu": "darwin",
+    },
+)
+
+config_setting(
+    name = "windows_x86_64",
+    values = {"cpu": "x64_windows"},
+)
+
+config_setting(
+    name = "android_armv7",
+    values = {
+        "crosstool_top": "//external:android/crosstool",
+        "cpu": "armeabi-v7a",
+    },
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
+    name = "android_arm64",
+    values = {
+        "crosstool_top": "//external:android/crosstool",
+        "cpu": "arm64-v8a",
+    },
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
+    name = "android_x86",
+    values = {
+        "crosstool_top": "//external:android/crosstool",
+        "cpu": "x86",
+    },
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
+    name = "android_x86_64",
+    values = {
+        "crosstool_top": "//external:android/crosstool",
+        "cpu": "x86_64",
+    },
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
+    name = "ios_armv7",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_armv7",
+    },
+)
+
+config_setting(
+    name = "ios_arm64",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_arm64",
+    },
+)
+
+config_setting(
+    name = "ios_arm64e",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_arm64e",
+    },
+)
+
+config_setting(
+    name = "macos_arm64",
+    values = {
+        "apple_platform_type": "macos",
+        "cpu": "darwin_arm64",
+    },
+)
+
+config_setting(
+    name = "ios_x86",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_i386",
+    },
+)
+
+config_setting(
+    name = "ios_x86_64",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_x86_64",
+    },
+)
+
+config_setting(
+    name = "ios_sim_arm64",
+    values = {
+        "apple_platform_type": "ios",
+        "cpu": "ios_sim_arm64",
+    },
+)
+
+config_setting(
+    name = "watchos_armv7k",
+    values = {
+        "apple_platform_type": "watchos",
+        "cpu": "watchos_armv7k",
+    },
+)
+
+config_setting(
+    name = "watchos_arm64_32",
+    values = {
+        "apple_platform_type": "watchos",
+        "cpu": "watchos_arm64_32",
+    },
+)
+
+config_setting(
+    name = "watchos_x86",
+    values = {
+        "apple_platform_type": "watchos",
+        "cpu": "watchos_i386",
+    },
+)
+
+config_setting(
+    name = "watchos_x86_64",
+    values = {
+        "apple_platform_type": "watchos",
+        "cpu": "watchos_x86_64",
+    },
+)
+
+config_setting(
+    name = "tvos_arm64",
+    values = {
+        "apple_platform_type": "tvos",
+        "cpu": "tvos_arm64",
+    },
+)
+
+config_setting(
+    name = "tvos_x86_64",
+    values = {
+        "apple_platform_type": "tvos",
+        "cpu": "tvos_x86_64",
+    },
+)
+
+config_setting(
+    name = "emscripten_wasm",
+    values = {
+        "cpu": "wasm",
+    },
+)
+
+config_setting(
+    name = "emscripten_wasmsimd",
+    values = {
+        "cpu": "wasm",
+        "features": "wasm_simd",
+    },
+)
+
+config_setting(
+    name = "emscripten_asmjs",
+    values = {
+        "cpu": "asmjs",
+    },
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e2d7d53..d7e1786 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,10 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 3.5 FATAL_ERROR)
 
-# ---[ Project and semantic versioning.
-PROJECT(cpuinfo C CXX)
+# ---[ Setup project
+PROJECT(
+    cpuinfo
+    LANGUAGES C CXX
+    )
 
 # ---[ Options.
 SET(CPUINFO_LIBRARY_TYPE "default" CACHE STRING "Type of cpuinfo library (shared, static, or default) to build")
@@ -14,6 +17,7 @@
 OPTION(CPUINFO_BUILD_UNIT_TESTS "Build cpuinfo unit tests" ON)
 OPTION(CPUINFO_BUILD_MOCK_TESTS "Build cpuinfo mock tests" ON)
 OPTION(CPUINFO_BUILD_BENCHMARKS "Build cpuinfo micro-benchmarks" ON)
+OPTION(CPUINFO_BUILD_PKG_CONFIG "Build pkg-config manifest" ON)
 
 # ---[ CMake options
 INCLUDE(GNUInstallDirs)
@@ -48,7 +52,7 @@
 
 # -- [ Determine target processor
 SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
-IF(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64)$")
+IF(IOS OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64)$"))
   SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}")
 ENDIF()
 
@@ -61,7 +65,7 @@
       "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
     SET(CPUINFO_SUPPORTED_PLATFORM FALSE)
   ENDIF()
-ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64)$")
+ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64|ARM64)$")
   MESSAGE(WARNING
     "Target processor architecture \"${CPUINFO_TARGET_PROCESSOR}\" is not supported in cpuinfo. "
     "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
@@ -167,6 +171,9 @@
       LIST(APPEND CPUINFO_SRCS
         src/arm/android/properties.c)
     ENDIF()
+  ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CPUINFO_TARGET_PROCESSOR STREQUAL "ARM64")
+    LIST(APPEND CPUINFO_SRCS src/arm/windows/init-by-logical-sys-info.c)
+    LIST(APPEND CPUINFO_SRCS src/arm/windows/init.c)
   ENDIF()
 
   IF(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
@@ -210,7 +217,7 @@
   TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _WIN32_WINNT=0x0601)
 ENDIF()
 SET_TARGET_PROPERTIES(cpuinfo PROPERTIES PUBLIC_HEADER include/cpuinfo.h)
-TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PUBLIC include)
+TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
 TARGET_INCLUDE_DIRECTORIES(cpuinfo BEFORE PRIVATE src)
 TARGET_INCLUDE_DIRECTORIES(cpuinfo_internals BEFORE PUBLIC include src)
 IF(CPUINFO_LOG_LEVEL STREQUAL "default")
@@ -260,11 +267,27 @@
 TARGET_LINK_LIBRARIES(cpuinfo PRIVATE clog)
 TARGET_LINK_LIBRARIES(cpuinfo_internals PRIVATE clog)
 
+ADD_LIBRARY(${PROJECT_NAME}::cpuinfo ALIAS cpuinfo)
+
+# support find_package(cpuinfo CONFIG)
+INCLUDE(CMakePackageConfigHelpers)
+GET_FILENAME_COMPONENT(CONFIG_FILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/cpuinfo-config.cmake ABSOLUTE)
+CONFIGURE_PACKAGE_CONFIG_FILE(
+  cmake/cpuinfo-config.cmake.in ${CONFIG_FILE_PATH}
+  INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME})
+INSTALL(FILES ${CONFIG_FILE_PATH}
+  DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}) # cpuinfo_DIR ${prefix}/share/cpuinfo
+
 INSTALL(TARGETS cpuinfo
+  EXPORT cpuinfo-targets
   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
   PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
+INSTALL(EXPORT cpuinfo-targets
+  NAMESPACE ${PROJECT_NAME}:: # IMPORTED cpuinfo::cpuinfo, cpuinfo::clog
+  DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME})
+
 # ---[ cpuinfo micro-benchmarks
 IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_BENCHMARKS)
   # ---[ Build google benchmark
@@ -301,7 +324,7 @@
 # ---[ cpuinfo mock library and mock tests
 IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_MOCK_TESTS)
   SET(CPUINFO_MOCK_SRCS "${CPUINFO_SRCS}")
-  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86_64)$")
+  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
     LIST(APPEND CPUINFO_MOCK_SRCS src/x86/mockcpuid.c)
   ENDIF()
   IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
@@ -326,406 +349,406 @@
     ADD_EXECUTABLE(atm7029b-tablet-test test/mock/atm7029b-tablet.cc)
     TARGET_INCLUDE_DIRECTORIES(atm7029b-tablet-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(atm7029b-tablet-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(atm7029b-tablet-test atm7029b-tablet-test)
+    ADD_TEST(NAME atm7029b-tablet-test COMMAND atm7029b-tablet-test)
 
     ADD_EXECUTABLE(blu-r1-hd-test test/mock/blu-r1-hd.cc)
     TARGET_INCLUDE_DIRECTORIES(blu-r1-hd-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(blu-r1-hd-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(blu-r1-hd-test blu-r1-hd-test)
+    ADD_TEST(NAME blu-r1-hd-test COMMAND blu-r1-hd-test)
 
     ADD_EXECUTABLE(galaxy-a3-2016-eu-test test/mock/galaxy-a3-2016-eu.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-a3-2016-eu-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-a3-2016-eu-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-a3-2016-eu-test galaxy-a3-2016-eu-test)
+    ADD_TEST(NAME galaxy-a3-2016-eu-test COMMAND galaxy-a3-2016-eu-test)
 
     ADD_EXECUTABLE(galaxy-a8-2016-duos-test test/mock/galaxy-a8-2016-duos.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-a8-2016-duos-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-a8-2016-duos-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-a8-2016-duos-test galaxy-a8-2016-duos-test)
+    ADD_TEST(NAME galaxy-a8-2016-duos-test COMMAND galaxy-a8-2016-duos-test)
 
     ADD_EXECUTABLE(galaxy-grand-prime-value-edition-test test/mock/galaxy-grand-prime-value-edition.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-grand-prime-value-edition-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-grand-prime-value-edition-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-grand-prime-value-edition-test galaxy-grand-prime-value-edition-test)
+    ADD_TEST(NAME galaxy-grand-prime-value-edition-test COMMAND galaxy-grand-prime-value-edition-test)
 
     ADD_EXECUTABLE(galaxy-j1-2016-test test/mock/galaxy-j1-2016.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-j1-2016-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-j1-2016-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-j1-2016-test galaxy-j1-2016-test)
+    ADD_TEST(NAME galaxy-j1-2016-test COMMAND galaxy-j1-2016-test)
 
     ADD_EXECUTABLE(galaxy-j5-test test/mock/galaxy-j5.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-j5-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-j5-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-j5-test galaxy-j5-test)
+    ADD_TEST(NAME galaxy-j5-test COMMAND galaxy-j5-test)
 
     ADD_EXECUTABLE(galaxy-j7-prime-test test/mock/galaxy-j7-prime.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-j7-prime-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-j7-prime-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-j7-prime-test galaxy-j7-prime-test)
+    ADD_TEST(NAME galaxy-j7-prime-test COMMAND galaxy-j7-prime-test)
 
     ADD_EXECUTABLE(galaxy-j7-tmobile-test test/mock/galaxy-j7-tmobile.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-j7-tmobile-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-j7-tmobile-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-j7-tmobile-test galaxy-j7-tmobile-test)
+    ADD_TEST(NAME galaxy-j7-tmobile-test COMMAND galaxy-j7-tmobile-test)
 
     ADD_EXECUTABLE(galaxy-j7-uae-test test/mock/galaxy-j7-uae.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-j7-uae-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-j7-uae-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-j7-uae-test galaxy-j7-uae-test)
+    ADD_TEST(NAME galaxy-j7-uae-test COMMAND galaxy-j7-uae-test)
 
     ADD_EXECUTABLE(galaxy-s3-us-test test/mock/galaxy-s3-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s3-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s3-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s3-us-test galaxy-s3-us-test)
+    ADD_TEST(NAME galaxy-s3-us-test COMMAND galaxy-s3-us-test)
 
     ADD_EXECUTABLE(galaxy-s4-us-test test/mock/galaxy-s4-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s4-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s4-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s4-us-test galaxy-s4-us-test)
+    ADD_TEST(NAME galaxy-s4-us-test COMMAND galaxy-s4-us-test)
 
     ADD_EXECUTABLE(galaxy-s5-global-test test/mock/galaxy-s5-global.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s5-global-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s5-global-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s5-global-test galaxy-s5-global-test)
+    ADD_TEST(NAME galaxy-s5-global-test COMMAND galaxy-s5-global-test)
 
     ADD_EXECUTABLE(galaxy-s5-us-test test/mock/galaxy-s5-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s5-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s5-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s5-us-test galaxy-s5-us-test)
+    ADD_TEST(NAME galaxy-s5-us-test COMMAND galaxy-s5-us-test)
 
     ADD_EXECUTABLE(galaxy-tab-3-7.0-test test/mock/galaxy-tab-3-7.0.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-tab-3-7.0-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-tab-3-7.0-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-tab-3-7.0-test galaxy-tab-3-7.0-test)
+    ADD_TEST(NAME galaxy-tab-3-7.0-test COMMAND galaxy-tab-3-7.0-test)
 
     ADD_EXECUTABLE(galaxy-tab-3-lite-test test/mock/galaxy-tab-3-lite.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-tab-3-lite-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-tab-3-lite-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-tab-3-lite-test galaxy-tab-3-lite-test)
+    ADD_TEST(NAME galaxy-tab-3-lite-test COMMAND galaxy-tab-3-lite-test)
 
     ADD_EXECUTABLE(galaxy-win-duos-test test/mock/galaxy-win-duos.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-win-duos-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-win-duos-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-win-duos-test galaxy-win-duos-test)
+    ADD_TEST(NAME galaxy-win-duos-test COMMAND galaxy-win-duos-test)
 
     ADD_EXECUTABLE(huawei-ascend-p7-test test/mock/huawei-ascend-p7.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-ascend-p7-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-ascend-p7-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-ascend-p7-test huawei-ascend-p7-test)
+    ADD_TEST(NAME huawei-ascend-p7-test COMMAND huawei-ascend-p7-test)
 
     ADD_EXECUTABLE(huawei-honor-6-test test/mock/huawei-honor-6.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-honor-6-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-honor-6-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-honor-6-test huawei-honor-6-test)
+    ADD_TEST(NAME huawei-honor-6-test COMMAND huawei-honor-6-test)
 
     ADD_EXECUTABLE(lenovo-a6600-plus-test test/mock/lenovo-a6600-plus.cc)
     TARGET_INCLUDE_DIRECTORIES(lenovo-a6600-plus-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(lenovo-a6600-plus-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(lenovo-a6600-plus-test lenovo-a6600-plus-test)
+    ADD_TEST(NAME lenovo-a6600-plus-test COMMAND lenovo-a6600-plus-test)
 
     ADD_EXECUTABLE(lenovo-vibe-x2-test test/mock/lenovo-vibe-x2.cc)
     TARGET_INCLUDE_DIRECTORIES(lenovo-vibe-x2-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(lenovo-vibe-x2-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(lenovo-vibe-x2-test lenovo-vibe-x2-test)
+    ADD_TEST(NAME lenovo-vibe-x2-test COMMAND lenovo-vibe-x2-test)
 
     ADD_EXECUTABLE(lg-k10-eu-test test/mock/lg-k10-eu.cc)
     TARGET_INCLUDE_DIRECTORIES(lg-k10-eu-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(lg-k10-eu-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(lg-k10-eu-test lg-k10-eu-test)
+    ADD_TEST(NAME lg-k10-eu-test COMMAND lg-k10-eu-test)
 
     ADD_EXECUTABLE(lg-optimus-g-pro-test test/mock/lg-optimus-g-pro.cc)
     TARGET_INCLUDE_DIRECTORIES(lg-optimus-g-pro-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(lg-optimus-g-pro-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(lg-optimus-g-pro-test lg-optimus-g-pro-test)
+    ADD_TEST(NAME lg-optimus-g-pro-test COMMAND lg-optimus-g-pro-test)
 
     ADD_EXECUTABLE(moto-e-gen1-test test/mock/moto-e-gen1.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-e-gen1-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-e-gen1-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-e-gen1-test moto-e-gen1-test)
+    ADD_TEST(NAME moto-e-gen1-test COMMAND moto-e-gen1-test)
 
     ADD_EXECUTABLE(moto-g-gen1-test test/mock/moto-g-gen1.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-g-gen1-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-g-gen1-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-g-gen1-test moto-g-gen1-test)
+    ADD_TEST(NAME moto-g-gen1-test COMMAND moto-g-gen1-test)
 
     ADD_EXECUTABLE(moto-g-gen2-test test/mock/moto-g-gen2.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-g-gen2-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-g-gen2-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-g-gen2-test moto-g-gen2-test)
+    ADD_TEST(NAME moto-g-gen2-test COMMAND moto-g-gen2-test)
 
     ADD_EXECUTABLE(moto-g-gen3-test test/mock/moto-g-gen3.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-g-gen3-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-g-gen3-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-g-gen3-test moto-g-gen3-test)
+    ADD_TEST(NAME moto-g-gen3-test COMMAND moto-g-gen3-test)
 
     ADD_EXECUTABLE(moto-g-gen4-test test/mock/moto-g-gen4.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-g-gen4-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-g-gen4-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-g-gen4-test moto-g-gen4-test)
+    ADD_TEST(NAME moto-g-gen4-test COMMAND moto-g-gen4-test)
 
     ADD_EXECUTABLE(moto-g-gen5-test test/mock/moto-g-gen5.cc)
     TARGET_INCLUDE_DIRECTORIES(moto-g-gen5-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(moto-g-gen5-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(moto-g-gen5-test moto-g-gen5-test)
+    ADD_TEST(NAME moto-g-gen5-test COMMAND moto-g-gen5-test)
 
     ADD_EXECUTABLE(nexus-s-test test/mock/nexus-s.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus-s-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus-s-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus-s-test nexus-s-test)
+    ADD_TEST(NAME nexus-s-test COMMAND nexus-s-test)
 
     ADD_EXECUTABLE(nexus4-test test/mock/nexus4.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus4-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus4-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus4-test nexus4-test)
+    ADD_TEST(NAME nexus4-test COMMAND nexus4-test)
 
     ADD_EXECUTABLE(nexus6-test test/mock/nexus6.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus6-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus6-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus6-test nexus6-test)
+    ADD_TEST(NAME nexus6-test COMMAND nexus6-test)
 
     ADD_EXECUTABLE(nexus10-test test/mock/nexus10.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus10-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus10-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus10-test nexus10-test)
+    ADD_TEST(NAME nexus10-test COMMAND nexus10-test)
 
     ADD_EXECUTABLE(padcod-10.1-test test/mock/padcod-10.1.cc)
     TARGET_INCLUDE_DIRECTORIES(padcod-10.1-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(padcod-10.1-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(padcod-10.1-test padcod-10.1-test)
+    ADD_TEST(NAME padcod-10.1-test COMMAND padcod-10.1-test)
 
     ADD_EXECUTABLE(xiaomi-redmi-2a-test test/mock/xiaomi-redmi-2a.cc)
     TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-2a-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xiaomi-redmi-2a-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xiaomi-redmi-2a-test xiaomi-redmi-2a-test)
+    ADD_TEST(NAME xiaomi-redmi-2a-test COMMAND xiaomi-redmi-2a-test)
 
     ADD_EXECUTABLE(xperia-sl-test test/mock/xperia-sl.cc)
     TARGET_INCLUDE_DIRECTORIES(xperia-sl-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xperia-sl-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xperia-sl-test xperia-sl-test)
+    ADD_TEST(NAME xperia-sl-test COMMAND xperia-sl-test)
   ENDIF()
 
   IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv5te|armv7-a|aarch64)$")
     ADD_EXECUTABLE(alcatel-revvl-test test/mock/alcatel-revvl.cc)
     TARGET_INCLUDE_DIRECTORIES(alcatel-revvl-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(alcatel-revvl-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(alcatel-revvl-test alcatel-revvl-test)
+    ADD_TEST(NAME alcatel-revvl-test COMMAND alcatel-revvl-test)
 
     ADD_EXECUTABLE(galaxy-a8-2018-test test/mock/galaxy-a8-2018.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-a8-2018-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-a8-2018-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-a8-2018-test galaxy-a8-2018-test)
+    ADD_TEST(NAME galaxy-a8-2018-test COMMAND galaxy-a8-2018-test)
 
     ADD_EXECUTABLE(galaxy-c9-pro-test test/mock/galaxy-c9-pro.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-c9-pro-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-c9-pro-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-c9-pro-test galaxy-c9-pro-test)
+    ADD_TEST(NAME galaxy-c9-pro-test COMMAND galaxy-c9-pro-test)
 
     ADD_EXECUTABLE(galaxy-s6-test test/mock/galaxy-s6.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s6-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s6-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s6-test galaxy-s6-test)
+    ADD_TEST(NAME galaxy-s6-test COMMAND galaxy-s6-test)
 
     ADD_EXECUTABLE(galaxy-s7-us-test test/mock/galaxy-s7-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s7-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s7-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s7-us-test galaxy-s7-us-test)
+    ADD_TEST(NAME galaxy-s7-us-test COMMAND galaxy-s7-us-test)
 
     ADD_EXECUTABLE(galaxy-s7-global-test test/mock/galaxy-s7-global.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s7-global-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s7-global-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s7-global-test galaxy-s7-global-test)
+    ADD_TEST(NAME galaxy-s7-global-test COMMAND galaxy-s7-global-test)
 
     ADD_EXECUTABLE(galaxy-s8-us-test test/mock/galaxy-s8-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s8-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s8-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s8-us-test galaxy-s8-us-test)
+    ADD_TEST(NAME galaxy-s8-us-test COMMAND galaxy-s8-us-test)
 
     ADD_EXECUTABLE(galaxy-s8-global-test test/mock/galaxy-s8-global.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s8-global-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s8-global-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s8-global-test galaxy-s8-global-test)
+    ADD_TEST(NAME galaxy-s8-global-test COMMAND galaxy-s8-global-test)
 
     ADD_EXECUTABLE(galaxy-s9-us-test test/mock/galaxy-s9-us.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s9-us-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s9-us-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s9-us-test galaxy-s9-us-test)
+    ADD_TEST(NAME galaxy-s9-us-test COMMAND galaxy-s9-us-test)
 
     ADD_EXECUTABLE(galaxy-s9-global-test test/mock/galaxy-s9-global.cc)
     TARGET_INCLUDE_DIRECTORIES(galaxy-s9-global-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(galaxy-s9-global-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(galaxy-s9-global-test galaxy-s9-global-test)
+    ADD_TEST(NAME galaxy-s9-global-test COMMAND galaxy-s9-global-test)
 
     ADD_EXECUTABLE(huawei-mate-8-test test/mock/huawei-mate-8.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-mate-8-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-mate-8-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-mate-8-test huawei-mate-8-test)
+    ADD_TEST(NAME huawei-mate-8-test COMMAND huawei-mate-8-test)
 
     ADD_EXECUTABLE(huawei-mate-9-test test/mock/huawei-mate-9.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-mate-9-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-mate-9-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-mate-9-test huawei-mate-9-test)
+    ADD_TEST(NAME huawei-mate-9-test COMMAND huawei-mate-9-test)
 
     ADD_EXECUTABLE(huawei-mate-10-test test/mock/huawei-mate-10.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-mate-10-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-mate-10-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-mate-10-test huawei-mate-10-test)
+    ADD_TEST(NAME huawei-mate-10-test COMMAND huawei-mate-10-test)
 
     ADD_EXECUTABLE(huawei-mate-20-test test/mock/huawei-mate-20.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-mate-20-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-mate-20-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-mate-20-test huawei-mate-20-test)
+    ADD_TEST(NAME huawei-mate-20-test COMMAND huawei-mate-20-test)
 
     ADD_EXECUTABLE(huawei-p8-lite-test test/mock/huawei-p8-lite.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-p8-lite-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-p8-lite-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-p8-lite-test huawei-p8-lite-test)
+    ADD_TEST(NAME huawei-p8-lite-test COMMAND huawei-p8-lite-test)
 
     ADD_EXECUTABLE(huawei-p9-lite-test test/mock/huawei-p9-lite.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-p9-lite-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-p9-lite-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-p9-lite-test huawei-p9-lite-test)
+    ADD_TEST(NAME huawei-p9-lite-test COMMAND huawei-p9-lite-test)
 
     ADD_EXECUTABLE(huawei-p20-pro-test test/mock/huawei-p20-pro.cc)
     TARGET_INCLUDE_DIRECTORIES(huawei-p20-pro-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(huawei-p20-pro-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(huawei-p20-pro-test huawei-p20-pro-test)
+    ADD_TEST(NAME huawei-p20-pro-test COMMAND huawei-p20-pro-test)
 
     ADD_EXECUTABLE(iconia-one-10-test test/mock/iconia-one-10.cc)
     TARGET_INCLUDE_DIRECTORIES(iconia-one-10-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(iconia-one-10-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(iconia-one-10-test iconia-one-10-test)
+    ADD_TEST(NAME iconia-one-10-test COMMAND iconia-one-10-test)
 
     ADD_EXECUTABLE(meizu-pro-6-test test/mock/meizu-pro-6.cc)
     TARGET_INCLUDE_DIRECTORIES(meizu-pro-6-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(meizu-pro-6-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(meizu-pro-6-test meizu-pro-6-test)
+    ADD_TEST(NAME meizu-pro-6-test COMMAND meizu-pro-6-test)
 
     ADD_EXECUTABLE(meizu-pro-6s-test test/mock/meizu-pro-6s.cc)
     TARGET_INCLUDE_DIRECTORIES(meizu-pro-6s-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(meizu-pro-6s-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(meizu-pro-6s-test meizu-pro-6s-test)
+    ADD_TEST(NAME meizu-pro-6s-test COMMAND meizu-pro-6s-test)
 
     ADD_EXECUTABLE(meizu-pro-7-plus-test test/mock/meizu-pro-7-plus.cc)
     TARGET_INCLUDE_DIRECTORIES(meizu-pro-7-plus-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(meizu-pro-7-plus-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(meizu-pro-7-plus-test meizu-pro-7-plus-test)
+    ADD_TEST(NAME meizu-pro-7-plus-test COMMAND meizu-pro-7-plus-test)
 
     ADD_EXECUTABLE(nexus5x-test test/mock/nexus5x.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus5x-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus5x-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus5x-test nexus5x-test)
+    ADD_TEST(NAME nexus5x-test COMMAND nexus5x-test)
 
     ADD_EXECUTABLE(nexus6p-test test/mock/nexus6p.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus6p-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus6p-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus6p-test nexus6p-test)
+    ADD_TEST(NAME nexus6p-test COMMAND nexus6p-test)
 
     ADD_EXECUTABLE(nexus9-test test/mock/nexus9.cc)
     TARGET_INCLUDE_DIRECTORIES(nexus9-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(nexus9-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(nexus9-test nexus9-test)
+    ADD_TEST(NAME nexus9-test COMMAND nexus9-test)
 
     ADD_EXECUTABLE(oneplus-3t-test test/mock/oneplus-3t.cc)
     TARGET_INCLUDE_DIRECTORIES(oneplus-3t-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oneplus-3t-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oneplus-3t-test oneplus-3t-test)
+    ADD_TEST(NAME oneplus-3t-test COMMAND oneplus-3t-test)
 
     ADD_EXECUTABLE(oneplus-5-test test/mock/oneplus-5.cc)
     TARGET_INCLUDE_DIRECTORIES(oneplus-5-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oneplus-5-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oneplus-5-test oneplus-5-test)
+    ADD_TEST(NAME oneplus-5-test COMMAND oneplus-5-test)
 
     ADD_EXECUTABLE(oneplus-5t-test test/mock/oneplus-5t.cc)
     TARGET_INCLUDE_DIRECTORIES(oneplus-5t-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oneplus-5t-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oneplus-5t-test oneplus-5t-test)
+    ADD_TEST(NAME oneplus-5t-test COMMAND oneplus-5t-test)
 
     ADD_EXECUTABLE(oppo-a37-test test/mock/oppo-a37.cc)
     TARGET_INCLUDE_DIRECTORIES(oppo-a37-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oppo-a37-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oppo-a37-test oppo-a37-test)
+    ADD_TEST(NAME oppo-a37-test COMMAND oppo-a37-test)
 
     ADD_EXECUTABLE(oppo-r9-test test/mock/oppo-r9.cc)
     TARGET_INCLUDE_DIRECTORIES(oppo-r9-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oppo-r9-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oppo-r9-test oppo-r9-test)
+    ADD_TEST(NAME oppo-r9-test COMMAND oppo-r9-test)
 
     ADD_EXECUTABLE(oppo-r15-test test/mock/oppo-r15.cc)
     TARGET_INCLUDE_DIRECTORIES(oppo-r15-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(oppo-r15-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(oppo-r15-test oppo-r15-test)
+    ADD_TEST(NAME oppo-r15-test COMMAND oppo-r15-test)
 
     ADD_EXECUTABLE(pixel-test test/mock/pixel.cc)
     TARGET_INCLUDE_DIRECTORIES(pixel-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(pixel-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(pixel-test pixel-test)
+    ADD_TEST(NAME pixel-test COMMAND pixel-test)
 
     ADD_EXECUTABLE(pixel-c-test test/mock/pixel-c.cc)
     TARGET_INCLUDE_DIRECTORIES(pixel-c-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(pixel-c-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(pixel-c-test pixel-c-test)
+    ADD_TEST(NAME pixel-c-test COMMAND pixel-c-test)
 
     ADD_EXECUTABLE(pixel-xl-test test/mock/pixel-xl.cc)
     TARGET_INCLUDE_DIRECTORIES(pixel-xl-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(pixel-xl-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(pixel-xl-test pixel-xl-test)
+    ADD_TEST(NAME pixel-xl-test COMMAND pixel-xl-test)
 
     ADD_EXECUTABLE(pixel-2-xl-test test/mock/pixel-2-xl.cc)
     TARGET_INCLUDE_DIRECTORIES(pixel-2-xl-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(pixel-2-xl-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(pixel-2-xl-test pixel-2-xl-test)
+    ADD_TEST(NAME pixel-2-xl-test COMMAND pixel-2-xl-test)
 
     ADD_EXECUTABLE(xiaomi-mi-5c-test test/mock/xiaomi-mi-5c.cc)
     TARGET_INCLUDE_DIRECTORIES(xiaomi-mi-5c-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xiaomi-mi-5c-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xiaomi-mi-5c-test xiaomi-mi-5c-test)
+    ADD_TEST(NAME xiaomi-mi-5c-test COMMAND xiaomi-mi-5c-test)
 
     ADD_EXECUTABLE(xiaomi-redmi-note-3-test test/mock/xiaomi-redmi-note-3.cc)
     TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-note-3-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xiaomi-redmi-note-3-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xiaomi-redmi-note-3-test xiaomi-redmi-note-3-test)
+    ADD_TEST(NAME xiaomi-redmi-note-3-test COMMAND xiaomi-redmi-note-3-test)
 
     ADD_EXECUTABLE(xiaomi-redmi-note-4-test test/mock/xiaomi-redmi-note-4.cc)
     TARGET_INCLUDE_DIRECTORIES(xiaomi-redmi-note-4-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xiaomi-redmi-note-4-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xiaomi-redmi-note-4-test xiaomi-redmi-note-4-test)
+    ADD_TEST(NAME xiaomi-redmi-note-4-test COMMAND xiaomi-redmi-note-4-test)
 
     ADD_EXECUTABLE(xperia-c4-dual-test test/mock/xperia-c4-dual.cc)
     TARGET_INCLUDE_DIRECTORIES(xperia-c4-dual-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(xperia-c4-dual-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(xperia-c4-dual-test xperia-c4-dual-test)
+    ADD_TEST(NAME xperia-c4-dual-test COMMAND xperia-c4-dual-test)
   ENDIF()
 
   IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i686|x86_64)$")
     ADD_EXECUTABLE(alldocube-iwork8-test test/mock/alldocube-iwork8.cc)
     TARGET_INCLUDE_DIRECTORIES(alldocube-iwork8-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(alldocube-iwork8-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(alldocube-iwork8-test alldocube-iwork8-test)
+    ADD_TEST(NAME alldocube-iwork8-test COMMAND alldocube-iwork8-test)
 
     ADD_EXECUTABLE(leagoo-t5c-test test/mock/leagoo-t5c.cc)
     TARGET_INCLUDE_DIRECTORIES(leagoo-t5c-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(leagoo-t5c-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(leagoo-t5c-test leagoo-t5c-test)
+    ADD_TEST(NAME leagoo-t5c-test COMMAND leagoo-t5c-test)
 
     ADD_EXECUTABLE(memo-pad-7-test test/mock/memo-pad-7.cc)
     TARGET_INCLUDE_DIRECTORIES(memo-pad-7-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(memo-pad-7-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(memo-pad-7-test memo-pad-7-test)
+    ADD_TEST(NAME memo-pad-7-test COMMAND memo-pad-7-test)
 
     ADD_EXECUTABLE(zenfone-c-test test/mock/zenfone-c.cc)
     TARGET_INCLUDE_DIRECTORIES(zenfone-c-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(zenfone-c-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(zenfone-c-test zenfone-c-test)
+    ADD_TEST(NAME zenfone-c-test COMMAND zenfone-c-test)
 
     ADD_EXECUTABLE(zenfone-2-test test/mock/zenfone-2.cc)
     TARGET_INCLUDE_DIRECTORIES(zenfone-2-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(zenfone-2-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(zenfone-2-test zenfone-2-test)
+    ADD_TEST(NAME zenfone-2-test COMMAND zenfone-2-test)
 
     ADD_EXECUTABLE(zenfone-2e-test test/mock/zenfone-2e.cc)
     TARGET_INCLUDE_DIRECTORIES(zenfone-2e-test BEFORE PRIVATE test/mock)
     TARGET_LINK_LIBRARIES(zenfone-2e-test PRIVATE cpuinfo_mock gtest)
-    ADD_TEST(zenfone-2e-test zenfone-2e-test)
+    ADD_TEST(NAME zenfone-2e-test COMMAND zenfone-2e-test)
   ENDIF()
 ENDIF()
 
@@ -735,22 +758,22 @@
   CPUINFO_TARGET_ENABLE_CXX11(init-test)
   CPUINFO_TARGET_RUNTIME_LIBRARY(init-test)
   TARGET_LINK_LIBRARIES(init-test PRIVATE cpuinfo gtest gtest_main)
-  ADD_TEST(init-test init-test)
+  ADD_TEST(NAME init-test COMMAND init-test)
 
   IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
     ADD_EXECUTABLE(get-current-test test/get-current.cc)
     CPUINFO_TARGET_ENABLE_CXX11(get-current-test)
     CPUINFO_TARGET_RUNTIME_LIBRARY(get-current-test)
     TARGET_LINK_LIBRARIES(get-current-test PRIVATE cpuinfo gtest gtest_main)
-    ADD_TEST(get-current-test get-current-test)
+    ADD_TEST(NAME get-current-test COMMAND get-current-test)
   ENDIF()
 
-  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86_64)$")
+  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
     ADD_EXECUTABLE(brand-string-test test/name/brand-string.cc)
     CPUINFO_TARGET_ENABLE_CXX11(brand-string-test)
     CPUINFO_TARGET_RUNTIME_LIBRARY(brand-string-test)
     TARGET_LINK_LIBRARIES(brand-string-test PRIVATE cpuinfo_internals gtest gtest_main)
-    ADD_TEST(brand-string-test brand-string-test)
+    ADD_TEST(NAME brand-string-test COMMAND brand-string-test)
   ENDIF()
 
   IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv[5-8].*|aarch64)$")
@@ -770,14 +793,14 @@
     CPUINFO_TARGET_ENABLE_CXX11(chipset-test)
     CPUINFO_TARGET_RUNTIME_LIBRARY(chipset-test)
     TARGET_LINK_LIBRARIES(chipset-test PRIVATE android_properties_interface gtest gtest_main)
-    ADD_TEST(chipset-test chipset-test)
+    ADD_TEST(NAME chipset-test COMMAND chipset-test)
 
     ADD_EXECUTABLE(cache-test test/arm-cache.cc)
     CPUINFO_TARGET_ENABLE_CXX11(cache-test)
     CPUINFO_TARGET_RUNTIME_LIBRARY(cache-test)
     TARGET_COMPILE_DEFINITIONS(cache-test PRIVATE __STDC_LIMIT_MACROS=1 __STDC_CONSTANT_MACROS=1)
     TARGET_LINK_LIBRARIES(cache-test PRIVATE cpuinfo_internals gtest gtest_main)
-    ADD_TEST(cache-test, cache-test)
+    ADD_TEST(NAME cache-test COMMAND cache-test)
   ENDIF()
 ENDIF()
 
@@ -812,7 +835,7 @@
     CPUINFO_TARGET_RUNTIME_LIBRARY(cpuinfo-dump)
   ENDIF()
 
-  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86_64)$")
+  IF(CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?)$")
     ADD_EXECUTABLE(cpuid-dump tools/cpuid-dump.c)
     CPUINFO_TARGET_ENABLE_C99(cpuid-dump)
     CPUINFO_TARGET_RUNTIME_LIBRARY(cpuid-dump)
@@ -821,3 +844,33 @@
     INSTALL(TARGETS cpuid-dump RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
   ENDIF()
 ENDIF()
+
+# ---[ pkg-config manifest. This is mostly from JsonCpp...
+IF(CPUINFO_BUILD_PKG_CONFIG)
+
+  FUNCTION(JOIN_PATHS joined_path first_path_segment)
+    SET(temp_path "${first_path_segment}")
+    FOREACH(current_segment IN LISTS ARGN)
+      IF(NOT ("${current_segment}" STREQUAL ""))
+        IF(IS_ABSOLUTE "${current_segment}")
+          SET(temp_path "${current_segment}")
+        ELSE()
+          SET(temp_path "${temp_path}/${current_segment}")
+        ENDIF()
+      ENDIF()
+    ENDFOREACH()
+    SET(${joined_path} "${temp_path}" PARENT_SCOPE)
+  ENDFUNCTION()
+
+  JOIN_PATHS(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
+  JOIN_PATHS(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
+
+  CONFIGURE_FILE(
+    "libcpuinfo.pc.in"
+    "libcpuinfo.pc"
+    @ONLY)
+
+  INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcpuinfo.pc"
+  DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+
+ENDIF()
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4bd525a
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies when
+an individual is representing the project or its community in public spaces.
+Examples of representing a project or community include using an official
+project e-mail address, posting via an official social media account, or acting
+as an appointed representative at an online or offline event. Representation of
+a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at <opensource-conduct@fb.com>. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..f519e26
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing to cpuinfo
+We want to make contributing to this project as easy and transparent as
+possible.
+
+## Pull Requests
+We actively welcome your pull requests.
+
+1. Fork the repo and create your branch from `master`.
+2. If you've added code that should be tested, add tests.
+3. If you've changed APIs, update the documentation.
+4. Ensure the test suite passes.
+5. Make sure your code lints.
+6. If you haven't already, complete the Contributor License Agreement ("CLA").
+
+## Contributor License Agreement ("CLA")
+In order to accept your pull request, we need you to submit a CLA. You only need
+to do this once to work on any of Facebook's open source projects.
+
+Complete your CLA here: <https://code.facebook.com/cla>
+
+## Issues
+We use GitHub issues to track public bugs. Please ensure your description is
+clear and has sufficient instructions to be able to reproduce the issue.
+
+Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
+disclosure of security bugs. In those cases, please go through the process
+outlined on that page and do not file a public issue.
+
+## License
+By contributing to cpuinfo, you agree that your contributions will be licensed
+under the LICENSE file in the root directory of this source tree.
\ No newline at end of file
diff --git a/METADATA b/METADATA
index 909e4ef..84f6958 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
     type: GIT
     value: "https://github.com/pytorch/cpuinfo"
   }
-  version: "5916273f79a21551890fd3d56fc5375a78d1598d"
+  version: "8ec7bd91ad0470e61cf38f618cc1f270dede599c"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2020
-    month: 12
-    day: 22
+    year: 2022
+    month: 9
+    day: 1
   }
 }
diff --git a/README.md b/README.md
index 97e65cd..87848b4 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@
 #endif
 ```
 
-Check if the host CPU support ARM NEON
+Check if the host CPU supports ARM NEON
 
 ```c
 cpuinfo_initialize();
@@ -100,6 +100,114 @@
 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_set);
 ```
 
+## Use via pkg-config
+
+If you would like to provide your project's build environment with the necessary compiler and linker flags in a portable manner, the library by default when built enables `CPUINFO_BUILD_PKG_CONFIG` and will generate a [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) manifest (_libcpuinfo.pc_). Here are several examples of how to use it:
+
+### Command Line
+
+If you used your distro's package manager to install the library, you can verify that it is available to your build environment like so:
+
+```console
+$ pkg-config --cflags --libs libcpuinfo
+-I/usr/include/x86_64-linux-gnu/ -L/lib/x86_64-linux-gnu/ -lcpuinfo
+```
+
+If you have installed the library from source into a non-standard prefix, pkg-config may need help finding it:
+
+```console
+$ PKG_CONFIG_PATH="/home/me/projects/cpuinfo/prefix/lib/pkgconfig/:$PKG_CONFIG_PATH" pkg-config --cflags --libs libcpuinfo
+-I/home/me/projects/cpuinfo/prefix/include -L/home/me/projects/cpuinfo/prefix/lib -lcpuinfo
+```
+
+### GNU Autotools
+
+To [use](https://autotools.io/pkgconfig/pkg_check_modules.html) with the GNU Autotools include the following snippet in your project's `configure.ac`:
+
+```makefile
+# CPU INFOrmation library...
+PKG_CHECK_MODULES(
+    [libcpuinfo], [libcpuinfo], [],
+    [AC_MSG_ERROR([libcpuinfo missing...])])
+YOURPROJECT_CXXFLAGS="$YOURPROJECT_CXXFLAGS $libcpuinfo_CFLAGS"
+YOURPROJECT_LIBS="$YOURPROJECT_LIBS $libcpuinfo_LIBS"
+```
+
+### Meson
+
+To use with Meson you just need to add `dependency('libcpuinfo')` as a dependency for your executable.
+
+```meson
+project(
+    'MyCpuInfoProject',
+    'cpp',
+    meson_version: '>=0.55.0'
+)
+
+executable(
+    'MyCpuInfoExecutable',
+    sources: 'main.cpp',
+    dependencies: dependency('libcpuinfo')
+)
+```
+
+### Bazel
+
+This project can be built using [Bazel](https://bazel.build/install). 
+
+You can also use this library as a dependency to your Bazel project. Add to the `WORKSPACE` file:
+
+```python
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+
+git_repository(
+    name = "org_pytorch_cpuinfo",
+    branch = "master",
+    remote = "https://github.com/Vertexwahn/cpuinfo.git",
+)
+```
+
+And to your `BUILD` file:
+
+```python
+cc_binary(
+    name = "cpuinfo_test",
+    srcs = [
+        # ...
+    ],
+    deps = [
+        "@org_pytorch_cpuinfo//:cpuinfo",
+    ],
+)
+```
+
+### CMake
+
+To use with CMake use the [FindPkgConfig](https://cmake.org/cmake/help/latest/module/FindPkgConfig.html) module. Here is an example:
+
+```cmake
+cmake_minimum_required(VERSION 3.6)
+project("MyCpuInfoProject")
+
+find_package(PkgConfig)
+pkg_check_modules(CpuInfo REQUIRED IMPORTED_TARGET libcpuinfo)
+
+add_executable(${PROJECT_NAME} main.cpp)
+target_link_libraries(${PROJECT_NAME} PkgConfig::CpuInfo)
+```
+
+### Makefile
+
+To use within a vanilla makefile, you can call pkg-config directly to supply compiler and linker flags using shell substitution.
+
+```makefile
+CFLAGS=-g3 -Wall -Wextra -Werror ...
+LDFLAGS=-lfoo ...
+...
+CFLAGS+= $(pkg-config --cflags libcpuinfo)
+LDFLAGS+= $(pkg-config --libs libcpuinfo)
+```
+
 ## Exposed information
 - [x] Processor (SoC) name
 - [x] Microarchitecture
@@ -142,12 +250,14 @@
   - [x] x86-64 (iPhone simulator)
   - [x] ARMv7
   - [x] ARM64
-- [x] OS X
+- [x] macOS
   - [x] x86
   - [x] x86-64
+  - [x] ARM64 (Apple silicon)
 - [x] Windows
   - [x] x86
   - [x] x86-64
+  - [x] arm64
 
 ## Methods
 
@@ -156,12 +266,13 @@
   - [x] Using `/proc/cpuinfo` on ARM
   - [x] Using `ro.chipname`, `ro.board.platform`, `ro.product.board`, `ro.mediatek.platform`, `ro.arch` properties (Android)
   - [ ] Using kernel log (`dmesg`) on ARM Linux
+  - [x] Using Windows registry on ARM64 Windows
 - Vendor and microarchitecture detection
   - [x] Intel-designed x86/x86-64 cores (up to Sunny Cove, Goldmont Plus, and Knights Mill)
   - [x] AMD-designed x86/x86-64 cores (up to Puma/Jaguar and Zen 2)
   - [ ] VIA-designed x86/x86-64 cores
   - [ ] Other x86 cores (DM&P, RDC, Transmeta, Cyrix, Rise)
-  - [x] ARM-designed ARM cores (up to Cortex-A55, Cortex-A77, and Neoverse E1/N1)
+  - [x] ARM-designed ARM cores (up to Cortex-A55, Cortex-A77, and Neoverse E1/N1/V1/N2)
   - [x] Qualcomm-designed ARM cores (Scorpion, Krait, and Kryo)
   - [x] Nvidia-designed ARM cores (Denver and Carmel)
   - [x] Samsung-designed ARM cores (Exynos)
@@ -178,6 +289,7 @@
   - [x] Using `/proc/self/auxv` (Android/ARM)
   - [ ] Using instruction probing on ARM (Linux)
   - [ ] Using CPUID registers on ARM64 (Linux)
+  - [x] Using IsProcessorFeaturePresent on ARM64 Windows
 - Cache detection
   - [x] Using CPUID leaf 0x00000002 (x86/x86-64)
   - [x] Using CPUID leaf 0x00000004 (non-AMD x86/x86-64)
@@ -189,6 +301,7 @@
   - [x] Using `sysctlbyname` (Mach)
   - [x] Using sysfs `typology` directories (ARM/Linux)
   - [ ] Using sysfs `cache` directories (Linux)
+  - [x] Using `GetLogicalProcessorInformationEx` on ARM64 Windows
 - TLB detection
   - [x] Using CPUID leaf 0x00000002 (x86/x86-64)
   - [ ] Using CPUID leaves 0x80000005-0x80000006 and 0x80000019 (AMD x86/x86-64)
@@ -202,3 +315,4 @@
   - [x] Using `GetLogicalProcessorInformationEx` (Windows)
   - [x] Using sysfs (Linux)
   - [x] Using chipset name (ARM/Linux)
+
diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel
new file mode 100644
index 0000000..24874c7
--- /dev/null
+++ b/WORKSPACE.bazel
@@ -0,0 +1 @@
+workspace(name = "org_pytorch_cpuinfo")
\ No newline at end of file
diff --git a/cmake/DownloadGoogleBenchmark.cmake b/cmake/DownloadGoogleBenchmark.cmake
index 59da7a6..fd99d0e 100644
--- a/cmake/DownloadGoogleBenchmark.cmake
+++ b/cmake/DownloadGoogleBenchmark.cmake
@@ -4,8 +4,8 @@
 
 INCLUDE(ExternalProject)
 ExternalProject_Add(googlebenchmark
-	URL https://github.com/google/benchmark/archive/v1.2.0.zip
-	URL_HASH SHA256=cc463b28cb3701a35c0855fbcefb75b29068443f1952b64dd5f4f669272e95ea
+	URL https://github.com/google/benchmark/archive/v1.6.1.zip
+	URL_HASH SHA256=367e963b8620080aff8c831e24751852cffd1f74ea40f25d9cc1b667a9dd5e45
 	SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googlebenchmark"
 	BINARY_DIR "${CONFU_DEPENDENCIES_BINARY_DIR}/googlebenchmark"
 	CONFIGURE_COMMAND ""
diff --git a/cmake/DownloadGoogleTest.cmake b/cmake/DownloadGoogleTest.cmake
index dc86c9c..c58fd09 100644
--- a/cmake/DownloadGoogleTest.cmake
+++ b/cmake/DownloadGoogleTest.cmake
@@ -4,8 +4,8 @@
 
 INCLUDE(ExternalProject)
 ExternalProject_Add(googletest
-	URL https://github.com/google/googletest/archive/release-1.10.0.zip
-	URL_HASH SHA256=94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91
+	URL https://github.com/google/googletest/archive/release-1.11.0.zip
+	URL_HASH SHA256=353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a
 	SOURCE_DIR "${CONFU_DEPENDENCIES_SOURCE_DIR}/googletest"
 	BINARY_DIR "${CONFU_DEPENDENCIES_BINARY_DIR}/googletest"
 	CONFIGURE_COMMAND ""
diff --git a/cmake/cpuinfo-config.cmake.in b/cmake/cpuinfo-config.cmake.in
new file mode 100644
index 0000000..fd52c8c
--- /dev/null
+++ b/cmake/cpuinfo-config.cmake.in
@@ -0,0 +1,12 @@
+@PACKAGE_INIT@
+
+get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+file(GLOB CONFIG_FILES "${_DIR}/cpuinfo-config-*.cmake")
+foreach(f ${CONFIG_FILES})
+  include(${f})
+endforeach()
+
+# ${_DIR}/cpuinfo-targets-*.cmake will be included here
+include("${_DIR}/cpuinfo-targets.cmake")
+
+check_required_components(@PROJECT_NAME@)
diff --git a/deps/clog/BUILD.bazel b/deps/clog/BUILD.bazel
new file mode 100644
index 0000000..7dc52ea
--- /dev/null
+++ b/deps/clog/BUILD.bazel
@@ -0,0 +1,58 @@
+# Copied from TensorFlow's `https://github.com/tensorflow/tensorflow/blob/master/third_party/clog/clog.BUILD
+# Licenced under Apache-2.0 License
+
+# Description:
+#   C-style (a-la printf) logging library
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+exports_files(["LICENSE"])
+
+cc_library(
+    name = "clog",
+    srcs = [
+        "src/clog.c",
+    ],
+    hdrs = [
+        "include/clog.h",
+    ],
+    copts = select({
+        ":windows": [],
+        "//conditions:default": ["-Wno-unused-result"],
+    }),
+    defines = select({
+        # When linkstatic=False, we need default visibility
+        ":macos_x86_64": ["CLOG_VISIBILITY="],
+        "//conditions:default": [],
+    }),
+    linkopts = select({
+        ":android": ["-llog"],
+        "//conditions:default": [],
+    }),
+    linkstatic = select({
+        # https://github.com/bazelbuild/bazel/issues/11552
+        ":macos_x86_64": False,
+        "//conditions:default": True,
+    }),
+    strip_include_prefix = "include",
+)
+
+config_setting(
+    name = "android",
+    values = {"crosstool_top": "//external:android/crosstool"},
+)
+
+config_setting(
+    name = "windows",
+    values = {"cpu": "x64_windows"},
+)
+
+config_setting(
+    name = "macos_x86_64",
+    values = {
+        "apple_platform_type": "macos",
+        "cpu": "darwin",
+    },
+)
diff --git a/deps/clog/CMakeLists.txt b/deps/clog/CMakeLists.txt
index 083f519..4f34d23 100644
--- a/deps/clog/CMakeLists.txt
+++ b/deps/clog/CMakeLists.txt
@@ -57,7 +57,7 @@
   C_EXTENSIONS NO)
 CLOG_TARGET_RUNTIME_LIBRARY(clog)
 SET_TARGET_PROPERTIES(clog PROPERTIES PUBLIC_HEADER include/clog.h)
-TARGET_INCLUDE_DIRECTORIES(clog BEFORE PUBLIC include)
+TARGET_INCLUDE_DIRECTORIES(clog PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
 IF(CLOG_LOG_TO_STDIO)
   TARGET_COMPILE_DEFINITIONS(clog PRIVATE CLOG_LOG_TO_STDIO=1)
 ELSE()
@@ -67,7 +67,10 @@
   TARGET_LINK_LIBRARIES(clog PRIVATE log)
 ENDIF()
 
+ADD_LIBRARY(cpuinfo::clog ALIAS clog)
+
 INSTALL(TARGETS clog
+  EXPORT cpuinfo-targets
   LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
   ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
   PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
diff --git a/deps/clog/src/clog.c b/deps/clog/src/clog.c
index fe5d43e..27658f9 100644
--- a/deps/clog/src/clog.c
+++ b/deps/clog/src/clog.c
@@ -10,6 +10,9 @@
 #ifdef __ANDROID__
 	#include <android/log.h>
 #endif
+#ifdef __hexagon__
+	#include <qurt_printf.h>
+#endif
 
 #ifndef CLOG_LOG_TO_STDIO
 	#ifdef __ANDROID__
@@ -102,12 +105,14 @@
 			out_buffer = heap_buffer;
 		}
 		out_buffer[prefix_chars + format_chars] = '\n';
-		#ifdef _WIN32
+		#if defined(_WIN32)
 			DWORD bytes_written;
 			WriteFile(
 				GetStdHandle(STD_ERROR_HANDLE),
 				out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
 				&bytes_written, NULL);
+		#elif defined(__hexagon__)
+			qurt_printf("%s", out_buffer);
 		#else
 			write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
 		#endif
@@ -178,12 +183,14 @@
 			out_buffer = heap_buffer;
 		}
 		out_buffer[prefix_chars + format_chars] = '\n';
-		#ifdef _WIN32
+		#if defined(_WIN32)
 			DWORD bytes_written;
 			WriteFile(
 				GetStdHandle(STD_ERROR_HANDLE),
 				out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
 				&bytes_written, NULL);
+		#elif defined(__hexagon__)
+			qurt_printf("%s", out_buffer);
 		#else
 			write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
 		#endif
@@ -254,12 +261,14 @@
 			out_buffer = heap_buffer;
 		}
 		out_buffer[prefix_chars + format_chars] = '\n';
-		#ifdef _WIN32
+		#if defined(_WIN32)
 			DWORD bytes_written;
 			WriteFile(
 				GetStdHandle(STD_ERROR_HANDLE),
 				out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
 				&bytes_written, NULL);
+		#elif defined(__hexagon__)
+			qurt_printf("%s", out_buffer);
 		#else
 			write(STDERR_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
 		#endif
@@ -330,12 +339,14 @@
 			out_buffer = heap_buffer;
 		}
 		out_buffer[prefix_chars + format_chars] = '\n';
-		#ifdef _WIN32
+		#if defined(_WIN32)
 			DWORD bytes_written;
 			WriteFile(
 				GetStdHandle(STD_OUTPUT_HANDLE),
 				out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
 				&bytes_written, NULL);
+		#elif defined(__hexagon__)
+			qurt_printf("%s", out_buffer);
 		#else
 			write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
 		#endif
@@ -406,12 +417,14 @@
 			out_buffer = heap_buffer;
 		}
 		out_buffer[prefix_chars + format_chars] = '\n';
-		#ifdef _WIN32
+		#if defined(_WIN32)
 			DWORD bytes_written;
 			WriteFile(
 				GetStdHandle(STD_OUTPUT_HANDLE),
 				out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH,
 				&bytes_written, NULL);
+		#elif defined(__hexagon__)
+			qurt_printf("%s", out_buffer);
 		#else
 			write(STDOUT_FILENO, out_buffer, prefix_chars + format_chars + CLOG_SUFFIX_LENGTH);
 		#endif
diff --git a/include/cpuinfo.h b/include/cpuinfo.h
index cffa299..bcbb3ec 100644
--- a/include/cpuinfo.h
+++ b/include/cpuinfo.h
@@ -426,9 +426,20 @@
 	cpuinfo_uarch_neoverse_n1  = 0x00300400,
 	/** ARM Neoverse E1. */
 	cpuinfo_uarch_neoverse_e1  = 0x00300401,
+	/** ARM Neoverse V1. */
+	cpuinfo_uarch_neoverse_v1  = 0x00300402,
+	/** ARM Neoverse N2. */
+	cpuinfo_uarch_neoverse_n2  = 0x00300403,
 
 	/** ARM Cortex-X1. */
-	cpuinfo_uarch_cortex_x1    = 0x00300500,
+	cpuinfo_uarch_cortex_x1    = 0x00300501,
+	/** ARM Cortex-X2. */
+	cpuinfo_uarch_cortex_x2    = 0x00300502,
+
+	/** ARM Cortex-A510. */
+	cpuinfo_uarch_cortex_a510  = 0x00300551,
+	/** ARM Cortex-A710. */
+	cpuinfo_uarch_cortex_a710  = 0x00300571,
 
 	/** Qualcomm Scorpion. */
 	cpuinfo_uarch_scorpion = 0x00400100,
@@ -489,10 +500,14 @@
 	cpuinfo_uarch_lightning = 0x00700109,
 	/** Apple A13 processor (little cores). */
 	cpuinfo_uarch_thunder   = 0x0070010A,
-	/** Apple M1 processor (big cores). */
+	/** Apple A14 / M1 processor (big cores). */
 	cpuinfo_uarch_firestorm = 0x0070010B,
-	/** Apple M1 processor (little cores). */
+	/** Apple A14 / M1 processor (little cores). */
 	cpuinfo_uarch_icestorm  = 0x0070010C,
+	/** Apple A15 / M2 processor (big cores). */
+	cpuinfo_uarch_avalanche = 0x0070010D,
+	/** Apple A15 / M2 processor (little cores). */
+	cpuinfo_uarch_blizzard  = 0x0070010E,
 
 	/** Cavium ThunderX. */
 	cpuinfo_uarch_thunderx = 0x00800100,
@@ -1460,14 +1475,17 @@
 		#endif
 		#if CPUINFO_ARCH_ARM64
 			bool atomics;
+			bool bf16;
 			bool sve;
 			bool sve2;
+			bool i8mm;
 		#endif
 		bool rdm;
 		bool fp16arith;
 		bool dot;
 		bool jscvt;
 		bool fcma;
+		bool fhm;
 
 		bool aes;
 		bool sha1;
@@ -1623,6 +1641,22 @@
 	#endif
 }
 
+static inline bool cpuinfo_has_arm_fp16_arith(void) {
+	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
+		return cpuinfo_isa.fp16arith;
+	#else
+		return false;
+	#endif
+}
+
+static inline bool cpuinfo_has_arm_bf16(void) {
+	#if CPUINFO_ARCH_ARM64
+		return cpuinfo_isa.bf16;
+	#else
+		return false;
+	#endif
+}
+
 static inline bool cpuinfo_has_arm_wmmx(void) {
 	#if CPUINFO_ARCH_ARM
 		return cpuinfo_isa.wmmx;
@@ -1705,9 +1739,9 @@
 	#endif
 }
 
-static inline bool cpuinfo_has_arm_fp16_arith(void) {
+static inline bool cpuinfo_has_arm_fhm(void) {
 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
-		return cpuinfo_isa.fp16arith;
+		return cpuinfo_isa.fhm;
 	#else
 		return false;
 	#endif
@@ -1721,6 +1755,14 @@
 	#endif
 }
 
+static inline bool cpuinfo_has_arm_neon_bf16(void) {
+	#if CPUINFO_ARCH_ARM64
+		return cpuinfo_isa.bf16;
+	#else
+		return false;
+	#endif
+}
+
 static inline bool cpuinfo_has_arm_jscvt(void) {
 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
 		return cpuinfo_isa.jscvt;
@@ -1737,6 +1779,14 @@
 	#endif
 }
 
+static inline bool cpuinfo_has_arm_i8mm(void) {
+	#if CPUINFO_ARCH_ARM64
+		return cpuinfo_isa.i8mm;
+	#else
+		return false;
+	#endif
+}
+
 static inline bool cpuinfo_has_arm_aes(void) {
 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
 		return cpuinfo_isa.aes;
@@ -1785,6 +1835,14 @@
 	#endif
 }
 
+static inline bool cpuinfo_has_arm_sve_bf16(void) {
+	#if CPUINFO_ARCH_ARM64
+		return cpuinfo_isa.sve && cpuinfo_isa.bf16;
+	#else
+		return false;
+	#endif
+}
+
 static inline bool cpuinfo_has_arm_sve2(void) {
 	#if CPUINFO_ARCH_ARM64
 		return cpuinfo_isa.sve2;
diff --git a/libcpuinfo.pc.in b/libcpuinfo.pc.in
new file mode 100644
index 0000000..3027da3
--- /dev/null
+++ b/libcpuinfo.pc.in
@@ -0,0 +1,12 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@libdir_for_pc_file@
+includedir=@includedir_for_pc_file@
+
+Name: lib@CMAKE_PROJECT_NAME@
+Description: Library to detect essential performance optimization information about host CPU.
+Version: 
+URL: @PROJECT_HOMEPAGE_URL@
+Libs: -L${libdir} -lcpuinfo
+Cflags: -I${includedir}
+
diff --git a/scripts/local-build.sh b/scripts/local-build.sh
new file mode 100755
index 0000000..426a13b
--- /dev/null
+++ b/scripts/local-build.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) Facebook, Inc. and its affiliates.
+# All rights reserved.
+#
+# This source code is licensed under the BSD-style license found in the
+# LICENSE file in the root directory of this source tree.
+
+set -e
+
+mkdir -p build/local
+
+CMAKE_ARGS=()
+
+# CMake-level configuration
+CMAKE_ARGS+=("-DCMAKE_BUILD_TYPE=Release")
+CMAKE_ARGS+=("-DCMAKE_POSITION_INDEPENDENT_CODE=ON")
+
+# If Ninja is installed, prefer it to Make
+if [ -x "$(command -v ninja)" ]
+then
+  CMAKE_ARGS+=("-GNinja")
+fi
+
+# Use-specified CMake arguments go last to allow overridding defaults
+CMAKE_ARGS+=($@)
+
+cd build/local && cmake ../.. \
+    "${CMAKE_ARGS[@]}"
+
+# Cross-platform parallel build
+if [ "$(uname)" == "Darwin" ]; then
+  cmake --build . -- "-j$(sysctl -n hw.ncpu)"
+elif [ "$(uname)" == "Linux" ]; then
+  cmake --build . -- "-j$(nproc)"
+else
+  cmake --build .
+fi
diff --git a/src/arm/cache.c b/src/arm/cache.c
index 446b02b..1a6dd38 100644
--- a/src/arm/cache.c
+++ b/src/arm/cache.c
@@ -535,6 +535,7 @@
 								l2_size = 1024 * 1024;
 								break;
 							case 660:
+							case 662:
 								/* Snapdragon 660: 1 MB L2 (little cores only) */
 								l2_size = 1024 * 1024;
 								break;
@@ -1238,6 +1239,63 @@
 			};
 			break;
 		}
+		case cpuinfo_uarch_neoverse_n1:
+		case cpuinfo_uarch_neoverse_v1:
+		case cpuinfo_uarch_neoverse_n2:
+		{
+                        /*
+                         * ARM Neoverse-n1 Core Technical Reference Manual
+                         * A6.1. About the L1 memory system
+			 *   The L1 memory system consists of separate instruction and data caches. Both have a fixed size of 64KB.
+                         *
+                         * A6.1.1 L1 instruction-side memory system
+                         *   The L1 instruction memory system has the following key features:
+                         *    - Virtually Indexed, Physically Tagged (VIPT), which behaves as a Physically Indexed,
+                         *      Physically Tagged (PIPT) 4-way set-associative L1 data cache.
+                         *    - Fixed cache line length of 64 bytes.
+                         *
+                         * A6.1.2 L1 data-side memory system
+                         *   The L1 data memory system has the following features:
+                         *    - Virtually Indexed, Physically Tagged (VIPT), which behaves as a Physically Indexed,
+                         *      Physically Tagged (PIPT) 4-way set-associative L1 data cache.
+                         *    - Fixed cache line length of 64 bytes.
+                         *    - Pseudo-LRU cache replacement policy.
+                         *
+                         * A7.1 About the L2 memory system
+                         *   The L2 memory subsystem consist of:
+			 *    - An 8-way set associative L2 cache with a configurable size of 256KB, 512KB, or 1024KB. Cache lines
+			 *      have a fixed length of 64 bytes.
+                         *    - Strictly inclusive with L1 data cache.
+			 *    - When configured with instruction cache hardware coherency, strictly inclusive with L1 instruction cache.
+			 *    - When configured without instruction cache hardware coherency, weakly inclusive with L1 instruction cache.
+                         */
+
+			const uint32_t min_l2_size_KB= 256;
+			const uint32_t min_l3_size_KB = 0;
+
+			*l1i = (struct cpuinfo_cache) {
+				.size = 64 * 1024,
+				.associativity = 4,
+				.line_size = 64,
+			};
+			*l1d = (struct cpuinfo_cache) {
+				.size = 64 * 1024,
+				.associativity = 4,
+				.line_size = 64,
+			};
+			*l2 = (struct cpuinfo_cache) {
+				.size = min_l2_size_KB * 1024,
+				.associativity = 8,
+				.line_size = 64,
+				.flags = CPUINFO_CACHE_INCLUSIVE,
+			};
+			*l3 = (struct cpuinfo_cache) {
+				.size = min_l3_size_KB * 1024,
+				.associativity = 16,
+				.line_size = 64,
+			};
+			break;
+		}
 #if CPUINFO_ARCH_ARM && !defined(__ARM_ARCH_8A__)
 		case cpuinfo_uarch_scorpion:
 			/*
@@ -1655,6 +1713,9 @@
 			 */
 			return 8 * 1024 * 1024;
 		case cpuinfo_uarch_cortex_a55:
+		case cpuinfo_uarch_neoverse_n1:
+		case cpuinfo_uarch_neoverse_v1:
+		case cpuinfo_uarch_neoverse_n2:
 		case cpuinfo_uarch_cortex_a75:
 		case cpuinfo_uarch_cortex_a76:
 		case cpuinfo_uarch_exynos_m4:
diff --git a/src/arm/linux/aarch32-isa.c b/src/arm/linux/aarch32-isa.c
index df68aa1..fb95ee9 100644
--- a/src/arm/linux/aarch32-isa.c
+++ b/src/arm/linux/aarch32-isa.c
@@ -64,6 +64,8 @@
 		 * - Processors with Exynos M4 cores
 		 * - Processors with Exynos M5 cores
 		 * - Neoverse N1 cores
+		 * - Neoverse V1 cores
+		 * - Neoverse N2 cores
 		 */
 		if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
 			/* Only little cores of Exynos 9810 support FP16 & RDM */
@@ -73,9 +75,11 @@
 				case UINT32_C(0x4100D050): /* Cortex-A55 */
 				case UINT32_C(0x4100D060): /* Cortex-A65 */
 				case UINT32_C(0x4100D0B0): /* Cortex-A76 */
-				case UINT32_C(0x4100D0C0): /* Neoverse N1 */
 				case UINT32_C(0x4100D0D0): /* Cortex-A77 */
 				case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
+				case UINT32_C(0x4100D460): /* Cortex-A510 */
+				case UINT32_C(0x4100D470): /* Cortex-A710 */
+				case UINT32_C(0x4100D480): /* Cortex-X2 */
 				case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
 				case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
 				case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
@@ -98,6 +102,9 @@
 			case UINT32_C(0x4100D0D0): /* Cortex-A77 */
 			case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
 			case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
+			case UINT32_C(0x4100D460): /* Cortex-A510 */
+			case UINT32_C(0x4100D470): /* Cortex-A710 */
+			case UINT32_C(0x4100D480): /* Cortex-X2 */
 			case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
 			case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
 			case UINT32_C(0x53000030): /* Exynos-M4 */
diff --git a/src/arm/linux/aarch64-isa.c b/src/arm/linux/aarch64-isa.c
index 2000e1a..44a8f4d 100644
--- a/src/arm/linux/aarch64-isa.c
+++ b/src/arm/linux/aarch64-isa.c
@@ -41,6 +41,8 @@
 	 * - Processors with Exynos M4 cores
 	 * - Processors with Exynos M5 cores
 	 * - Neoverse N1 cores
+	 * - Neoverse V1 cores
+	 * - Neoverse N2 cores
 	 */
 	if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
 		/* Exynos 9810 reports that it supports FP16 compute, but in fact only little cores do */
@@ -54,6 +56,8 @@
 			case UINT32_C(0x4100D0C0): /* Neoverse N1 */
 			case UINT32_C(0x4100D0D0): /* Cortex-A77 */
 			case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
+			case UINT32_C(0x4100D400): /* Neoverse V1 */
+			case UINT32_C(0x4100D490): /* Neoverse N2 */
 			case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
 			case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
 			case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
@@ -78,6 +82,9 @@
 				break;
 		}
 	}
+	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_I8MM) {
+		isa->i8mm = true;
+	}
 
 	/*
 	 * Many phones ship with an old kernel configuration that doesn't report UDOT/SDOT instructions.
@@ -89,6 +96,8 @@
 		case UINT32_C(0x4100D0C0): /* Neoverse N1 */
 		case UINT32_C(0x4100D0D0): /* Cortex-A77 */
 		case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
+		case UINT32_C(0x4100D400): /* Neoverse V1 */
+		case UINT32_C(0x4100D490): /* Neoverse N2 */
 		case UINT32_C(0x4100D4A0): /* Neoverse E1 */
 		case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
 		case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
@@ -124,4 +133,13 @@
 	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) {
 		isa->sve2 = true;
 	}
+	// SVEBF16 is set iff SVE and BF16 are both supported, but the SVEBF16 feature flag
+	// was added in Linux kernel before the BF16 feature flag, so we check for either.
+	if (features2 & (CPUINFO_ARM_LINUX_FEATURE2_BF16 | CPUINFO_ARM_LINUX_FEATURE2_SVEBF16)) {
+		isa->bf16 = true;
+	}
+	if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM) {
+		isa->fhm = true;
+	}
 }
+
diff --git a/src/arm/linux/chipset.c b/src/arm/linux/chipset.c
index e36283c..f2a002d 100644
--- a/src/arm/linux/chipset.c
+++ b/src/arm/linux/chipset.c
@@ -1,3 +1,4 @@
+#include <ctype.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -281,6 +282,82 @@
 	return true;
 }
 
+
+struct special_map_entry {
+	const char* platform;
+	uint16_t model;
+	uint8_t series;
+	char suffix;
+};
+
+static const struct special_map_entry qualcomm_hardware_map_entries[] = {
+		{
+				/* "Kona" -> Qualcomm Kona */
+				.platform = "Kona",
+				.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
+				.model = 865,
+		},
+		{
+				/* "Bengal" -> Qualcomm Bengal */
+				.platform = "Bengal",
+				.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
+				.model = 662,
+		},
+		{
+				/* "Bengalp" -> Qualcomm Bengalp */
+				.platform = "Bengalp",
+				.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
+				.model = 662,
+		},
+		{
+				/* "Lito" -> Qualcomm Lito */
+				.platform = "Lito",
+				.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
+				.model = 765,
+				.suffix = 'G'
+		},
+		{
+				/* "Lagoon" -> Qualcomm Lagoon */
+				.platform = "Lagoon",
+				.series = cpuinfo_arm_chipset_series_qualcomm_snapdragon,
+				.model = 0,
+		},
+};
+
+
+int strcicmp(char const *a, char const *b)
+{
+	for (;; a++, b++) {
+		int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
+		if (d != 0 || !*a)
+			return d;
+	}
+}
+
+static bool match_qualcomm_special(
+		const char* start, const char* end,
+		struct cpuinfo_arm_chipset chipset[restrict static 1])
+{
+	for (size_t i = 0; i < CPUINFO_COUNT_OF(qualcomm_hardware_map_entries); i++) {
+		int length = end - start;
+		if (strcicmp(qualcomm_hardware_map_entries[i].platform, start) == 0 &&
+			qualcomm_hardware_map_entries[i].platform[length] == 0)
+		{
+			*chipset = (struct cpuinfo_arm_chipset) {
+					.vendor = chipset_series_vendor[qualcomm_hardware_map_entries[i].series],
+					.series = (enum cpuinfo_arm_chipset_series) qualcomm_hardware_map_entries[i].series,
+					.model = qualcomm_hardware_map_entries[i].model,
+					.suffix = {
+							[0] = qualcomm_hardware_map_entries[i].suffix,
+					},
+			};
+			return true;
+		}
+	}
+	return false;
+
+}
+
 /**
  * Tries to match /Samsung Exynos\d{4}$/ signature (case-insensitive) for Samsung Exynos chipsets.
  * If match successful, extracts model information into \p chipset argument.
@@ -1351,7 +1428,7 @@
 		return false;
 	}
 
-	/* Compare sunXi platform id and number of cores to tabluted values to decode chipset name */
+	/* Compare sunXi platform id and number of cores to tabulated values to decode chipset name */
 	uint32_t model = 0;
 	char suffix = 0;
 	for (size_t i = 0; i < CPUINFO_COUNT_OF(sunxi_map_entries); i++) {
@@ -1752,13 +1829,6 @@
 	return (length == 5 || start[5] == '3');
 }
 
-struct special_map_entry {
-	const char* platform;
-	uint16_t model;
-	uint8_t series;
-	char suffix;
-};
-
 static const struct special_map_entry special_hardware_map_entries[] = {
 #if CPUINFO_ARCH_ARM
 	{
@@ -2317,6 +2387,14 @@
 								(int) hardware_length, hardware);
 							return chipset;
 						}
+
+						if (match_qualcomm_special(pos, hardware_end, &chipset)) {
+							cpuinfo_log_debug(
+									"matched Qualcomm signature in /proc/cpuinfo Hardware string \"%.*s\"",
+									(int) hardware_length, hardware);
+							return chipset;
+						}
+
 					}
 					word_start = false;
 					break;
diff --git a/src/arm/linux/clusters.c b/src/arm/linux/clusters.c
index c7a4045..430773d 100644
--- a/src/arm/linux/clusters.c
+++ b/src/arm/linux/clusters.c
@@ -48,7 +48,7 @@
  * @param usable_processors - number of processors in the @p processors array with CPUINFO_LINUX_FLAG_VALID flags.
  * @param max_processors - number of elements in the @p processors array.
  * @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum
- *                             frequency, MIDR infromation, and core cluster (package siblings list) information.
+ *                             frequency, MIDR information, and core cluster (package siblings list) information.
  *
  * @retval true if the heuristic successfully assigned all processors into clusters of cores.
  * @retval false if known details about processors contradict the heuristic configuration of core clusters.
@@ -292,9 +292,9 @@
  *   - Processors assigned to these clusters stay assigned to the same clusters
  *   - No new processors are added to these clusters
  * - Processors without pre-assigned cluster are clustered in one sequential scan:
- *   - If known details (min/max frequency, MIDR components) of a processor are compatible with a preceeding
- *     processor, without pre-assigned cluster, the processor is assigned to the cluster of the preceeding processor.
- *   - If known details (min/max frequency, MIDR components) of a processor are not compatible with a preceeding
+ *   - If known details (min/max frequency, MIDR components) of a processor are compatible with a preceding
+ *     processor, without pre-assigned cluster, the processor is assigned to the cluster of the preceding processor.
+ *   - If known details (min/max frequency, MIDR components) of a processor are not compatible with a preceding
  *     processor, the processor is assigned to a newly created cluster.
  *
  * The function must be called after parsing OS-provided information on core clusters, and usually is called only
@@ -309,7 +309,7 @@
  *
  * @param max_processors - number of elements in the @p processors array.
  * @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum
- *                             frequency, MIDR infromation, and core cluster (package siblings list) information.
+ *                             frequency, MIDR information, and core cluster (package siblings list) information.
  *
  * @retval true if the heuristic successfully assigned all processors into clusters of cores.
  * @retval false if known details about processors contradict the heuristic configuration of core clusters.
@@ -331,7 +331,7 @@
 				if (cluster_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) {
 					if (cluster_min_frequency != processors[i].min_frequency) {
 						cpuinfo_log_info(
-							"minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceeding cluster (%"PRIu32" KHz); "
+							"minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); "
 							"processor %"PRIu32" starts to a new cluster",
 							i, processors[i].min_frequency, cluster_min_frequency, i);
 						goto new_cluster;
@@ -346,7 +346,7 @@
 				if (cluster_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
 					if (cluster_max_frequency != processors[i].max_frequency) {
 						cpuinfo_log_debug(
-							"maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceeding cluster (%"PRIu32" KHz); "
+							"maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); "
 							"processor %"PRIu32" starts a new cluster",
 							i, processors[i].max_frequency, cluster_max_frequency, i);
 						goto new_cluster;
@@ -361,7 +361,7 @@
 				if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
 					if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) {
 						cpuinfo_log_debug(
-							"CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of preceeding cluster (0x%02"PRIx32"); "
+							"CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of preceding cluster (0x%02"PRIx32"); "
 							"processor %"PRIu32" starts to a new cluster",
 							i, midr_get_implementer(processors[i].midr), midr_get_implementer(cluster_midr), i);
 						goto new_cluster;
@@ -417,11 +417,11 @@
 				}
 			}
 
-			/* All checks passed, attach processor to the preceeding cluster */
+			/* All checks passed, attach processor to the preceding cluster */
 			cluster_processors++;
 			processors[i].package_leader_id = cluster_start;
 			processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
-			cpuinfo_log_debug("assigned processor %"PRIu32" to preceeding cluster of processor %"PRIu32, i, cluster_start);
+			cpuinfo_log_debug("assigned processor %"PRIu32" to preceding cluster of processor %"PRIu32, i, cluster_start);
 			continue;
 
 new_cluster:
diff --git a/src/arm/linux/cpuinfo.c b/src/arm/linux/cpuinfo.c
index 90e1631..817da12 100644
--- a/src/arm/linux/cpuinfo.c
+++ b/src/arm/linux/cpuinfo.c
@@ -177,6 +177,10 @@
 					#if CPUINFO_ARCH_ARM64
 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_FCMA;
 					#endif
+				} else if (memcmp(feature_start, "i8mm", feature_length) == 0) {
+					#if CPUINFO_ARCH_ARM64
+						processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_I8MM;
+					#endif
 #if CPUINFO_ARCH_ARM
 				} else if (memcmp(feature_start, "half", feature_length) == 0) {
 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_HALF;
@@ -283,6 +287,10 @@
 					#if CPUINFO_ARCH_ARM64
 						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM;
 					#endif
+				} else if (memcmp(feature_start, "asimdfhm", feature_length) == 0) {
+					#if CPUINFO_ARCH_ARM64
+						processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM;
+					#endif
 #if CPUINFO_ARCH_ARM
 				} else if (memcmp(feature_start, "fastmult", feature_length) == 0) {
 					processor->features |= CPUINFO_ARM_LINUX_FEATURE_FASTMULT;
diff --git a/src/arm/linux/init.c b/src/arm/linux/init.c
index 23d8439..d3da5a9 100644
--- a/src/arm/linux/init.c
+++ b/src/arm/linux/init.c
@@ -510,7 +510,7 @@
 	uint32_t l2_count = 0, l3_count = 0, big_l3_size = 0, cluster_id = UINT32_MAX;
 	/* Indication whether L3 (if it exists) is shared between all cores */
 	bool shared_l3 = true;
-	/* Populate cache infromation structures in l1i, l1d */
+	/* Populate cache information structures in l1i, l1d */
 	for (uint32_t i = 0; i < valid_processors; i++) {
 		if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
 			cluster_id += 1;
diff --git a/src/arm/linux/midr.c b/src/arm/linux/midr.c
index 2c3116b..0d8f03f 100644
--- a/src/arm/linux/midr.c
+++ b/src/arm/linux/midr.c
@@ -675,10 +675,10 @@
 
 /*
  * Initializes MIDR for leaders of core clusters in a single sequential scan:
- *  - Clusters preceeding the first reported MIDR value are assumed to have default MIDR value.
+ *  - Clusters preceding the first reported MIDR value are assumed to have default MIDR value.
  *  - Clusters following any reported MIDR value to have that MIDR value.
  *
- * @param default_midr - MIDR value that will be assigned to cluster leaders preceeding any reported MIDR value.
+ * @param default_midr - MIDR value that will be assigned to cluster leaders preceding any reported MIDR value.
  * @param processors_count - number of logical processor descriptions in the @p processors array.
  * @param[in,out] processors - array of logical processor descriptions with pre-parsed MIDR, maximum frequency,
  *                             and decoded core cluster (package_leader_id) information.
@@ -833,7 +833,7 @@
 			 * 2. For systems with 2 clusters and MIDR known for one cluster, assume big.LITTLE configuration,
 			 *    and estimate MIDR for the other cluster under assumption that MIDR for the big cluster is known.
 			 * 3. Initialize MIDRs for core clusters in a single sequential scan:
-			 *    - Clusters preceeding the first reported MIDR value are assumed to have the last reported MIDR value.
+			 *    - Clusters preceding the first reported MIDR value are assumed to have the last reported MIDR value.
 			 *    - Clusters following any reported MIDR value to have that MIDR value.
 			 */
 
diff --git a/src/arm/mach/init.c b/src/arm/mach/init.c
index dbea578..6a28b2d 100644
--- a/src/arm/mach/init.c
+++ b/src/arm/mach/init.c
@@ -15,43 +15,25 @@
 #include <cpuinfo/log.h>
 
 /* Polyfill recent CPUFAMILY_ARM_* values for older SDKs */
-#ifndef CPUFAMILY_ARM_MONSOON_MISTRAL
-	#define CPUFAMILY_ARM_MONSOON_MISTRAL   0xE81E7EF6
-#endif
 #ifndef CPUFAMILY_ARM_VORTEX_TEMPEST
-	#define CPUFAMILY_ARM_VORTEX_TEMPEST    0x07D34B9F
+	#define CPUFAMILY_ARM_VORTEX_TEMPEST     0x07D34B9F
 #endif
 #ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER
-	#define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504D2
+	#define CPUFAMILY_ARM_LIGHTNING_THUNDER  0x462504D2
 #endif
 #ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM
 	#define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1B588BB3
 #endif
+#ifndef CPUFAMILY_ARM_AVALANCHE_BLIZZARD
+	#define CPUFAMILY_ARM_AVALANCHE_BLIZZARD 0xDA33D83D
+#endif
 
 struct cpuinfo_arm_isa cpuinfo_isa = {
-#if CPUINFO_ARCH_ARM
-	.thumb = true,
-	.thumb2 = true,
-	.thumbee = false,
-	.jazelle = false,
-	.armv5e = true,
-	.armv6 = true,
-	.armv6k = true,
-	.armv7 = true,
-	.vfpv2 = false,
-	.vfpv3 = true,
-	.d32 = true,
-	.wmmx = false,
-	.wmmx2 = false,
-	.neon = true,
-#endif
-#if CPUINFO_ARCH_ARM64
 	.aes = true,
 	.sha1 = true,
 	.sha2 = true,
 	.pmull = true,
 	.crc32 = true,
-#endif
 };
 
 static uint32_t get_sys_info(int type_specifier, const char* name) {
@@ -83,10 +65,8 @@
 	return result;
 }
 
-static enum cpuinfo_uarch decode_uarch(uint32_t cpu_family, uint32_t cpu_subtype, uint32_t core_index, uint32_t core_count) {
+static enum cpuinfo_uarch decode_uarch(uint32_t cpu_family, uint32_t core_index, uint32_t core_count) {
 	switch (cpu_family) {
-		case CPUFAMILY_ARM_SWIFT:
-			return cpuinfo_uarch_swift;
 		case CPUFAMILY_ARM_CYCLONE:
 			return cpuinfo_uarch_cyclone;
 		case CPUFAMILY_ARM_TYPHOON:
@@ -107,25 +87,15 @@
 		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
 			/* Hexa-core: 2x Firestorm + 4x Icestorm; Octa-core: 4x Firestorm + 4x Icestorm */
 			return core_index + 4 < core_count ? cpuinfo_uarch_firestorm : cpuinfo_uarch_icestorm;
+		case CPUFAMILY_ARM_AVALANCHE_BLIZZARD:
+			/* Hexa-core: 2x Avalanche + 4x Blizzard */
+			return core_index + 4 < core_count ? cpuinfo_uarch_avalanche : cpuinfo_uarch_blizzard;
 		default:
 			/* Use hw.cpusubtype for detection */
 			break;
 	}
 
-	#if CPUINFO_ARCH_ARM
-		switch (cpu_subtype) {
-			case CPU_SUBTYPE_ARM_V7:
-				return cpuinfo_uarch_cortex_a8;
-			case CPU_SUBTYPE_ARM_V7F:
-				return cpuinfo_uarch_cortex_a9;
-			case CPU_SUBTYPE_ARM_V7K:
-				return cpuinfo_uarch_cortex_a7;
-			default:
-				return cpuinfo_uarch_unknown;
-		}
-	#else
-		return cpuinfo_uarch_unknown;
-	#endif
+	return cpuinfo_uarch_unknown;
 }
 
 static void decode_package_name(char* package_name) {
@@ -299,71 +269,118 @@
 
 
 	const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily");
-	const uint32_t cpu_type = get_sys_info_by_name("hw.cputype");
-	const uint32_t cpu_subtype = get_sys_info_by_name("hw.cpusubtype");
-	switch (cpu_type) {
-		case CPU_TYPE_ARM64:
-			cpuinfo_isa.aes = true;
-			cpuinfo_isa.sha1 = true;
-			cpuinfo_isa.sha2 = true;
-			cpuinfo_isa.pmull = true;
-			cpuinfo_isa.crc32 = true;
-			break;
-#if CPUINFO_ARCH_ARM
-		case CPU_TYPE_ARM:
-			switch (cpu_subtype) {
-				case CPU_SUBTYPE_ARM_V8:
-					cpuinfo_isa.armv8 = true;
-					cpuinfo_isa.aes = true;
-					cpuinfo_isa.sha1 = true;
-					cpuinfo_isa.sha2 = true;
-					cpuinfo_isa.pmull = true;
-					cpuinfo_isa.crc32 = true;
-					/* Fall-through to add ARMv7S features */
-				case CPU_SUBTYPE_ARM_V7S:
-				case CPU_SUBTYPE_ARM_V7K:
-					cpuinfo_isa.fma = true;
-					/* Fall-through to add ARMv7F features */
-				case CPU_SUBTYPE_ARM_V7F:
-					cpuinfo_isa.armv7mp = true;
-					cpuinfo_isa.fp16 = true;
-					/* Fall-through to add ARMv7 features */
-				case CPU_SUBTYPE_ARM_V7:
-					break;
-				default:
-					break;
-			}
-			break;
-#endif
-	}
-	/*
-	 * Support for ARMv8.1 Atomics & FP16 arithmetic instructions is supposed to be detected via
-	 * sysctlbyname calls with "hw.optional.armv8_1_atomics" and "hw.optional.neon_fp16" arguments
-	 * (see https://devstreaming-cdn.apple.com/videos/wwdc/2018/409t8zw7rumablsh/409/409_whats_new_in_llvm.pdf),
-	 * but on new iOS versions these calls just fail with EPERM.
-	 *
-	 * Thus, we whitelist CPUs known to support these instructions.
-	 */
-	switch (cpu_family) {
-		case CPUFAMILY_ARM_MONSOON_MISTRAL:
-		case CPUFAMILY_ARM_VORTEX_TEMPEST:
-		case CPUFAMILY_ARM_LIGHTNING_THUNDER:
-		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
-			#if CPUINFO_ARCH_ARM64
-				cpuinfo_isa.atomics = true;
-			#endif
-			cpuinfo_isa.fp16arith = true;
-	}
 
 	/*
-	 * There does not yet seem to exist an OS mechanism to detect support for
-	 * ARMv8.2 optional dot-product instructions, so we currently whitelist CPUs
-	 * known to support these instruction.
+	 * iOS 15 and macOS 12 added sysctls for ARM features, use them where possible.
+	 * Otherwise, fallback to hardcoded set of CPUs with known support.
 	 */
-	switch (cpu_family) {
-		case CPUFAMILY_ARM_LIGHTNING_THUNDER:
-		case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
-			cpuinfo_isa.dot = true;
+	const uint32_t has_feat_lse = get_sys_info_by_name("hw.optional.arm.FEAT_LSE");
+	if (has_feat_lse != 0) {
+		cpuinfo_isa.atomics = true;
+	} else {
+		// Mandatory in ARMv8.1-A, list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_MONSOON_MISTRAL:
+			case CPUFAMILY_ARM_VORTEX_TEMPEST:
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.atomics = true;
+		}
+	}
+
+	const uint32_t has_feat_rdm = get_sys_info_by_name("hw.optional.arm.FEAT_RDM");
+	if (has_feat_rdm != 0) {
+		cpuinfo_isa.rdm = true;
+	} else {
+		// Optional in ARMv8.2-A (implemented in Apple cores),
+		// list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_MONSOON_MISTRAL:
+			case CPUFAMILY_ARM_VORTEX_TEMPEST:
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.rdm = true;
+		}
+	}
+
+	const uint32_t has_feat_fp16 = get_sys_info_by_name("hw.optional.arm.FEAT_FP16");
+	if (has_feat_fp16 != 0) {
+		cpuinfo_isa.fp16arith = true;
+	} else {
+		// Optional in ARMv8.2-A (implemented in Apple cores),
+		// list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_MONSOON_MISTRAL:
+			case CPUFAMILY_ARM_VORTEX_TEMPEST:
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.fp16arith = true;
+		}
+	}
+
+	const uint32_t has_feat_fhm = get_sys_info_by_name("hw.optional.arm.FEAT_FHM");
+	if (has_feat_fhm != 0) {
+		cpuinfo_isa.fhm = true;
+	} else {
+		// Prior to iOS 15, use 'hw.optional.armv8_2_fhm'
+		const uint32_t has_feat_fhm_legacy = get_sys_info_by_name("hw.optional.armv8_2_fhm");
+		if (has_feat_fhm_legacy != 0) {
+			cpuinfo_isa.fhm = true;
+		} else {
+			// Mandatory in ARMv8.4-A when FP16 arithmetics is implemented,
+			// list only cores released before iOS 15 / macOS 12
+			switch (cpu_family) {
+				case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+				case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+					cpuinfo_isa.fhm = true;
+			}
+		}
+	}
+
+	const uint32_t has_feat_bf16 = get_sys_info_by_name("hw.optional.arm.FEAT_BF16");
+	if (has_feat_bf16 != 0) {
+		cpuinfo_isa.bf16 = true;
+	}
+
+	const uint32_t has_feat_fcma = get_sys_info_by_name("hw.optional.arm.FEAT_FCMA");
+	if (has_feat_fcma != 0) {
+		cpuinfo_isa.fcma = true;
+	} else {
+		// Mandatory in ARMv8.3-A, list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.fcma = true;
+		}
+	}
+
+	const uint32_t has_feat_jscvt = get_sys_info_by_name("hw.optional.arm.FEAT_JSCVT");
+	if (has_feat_jscvt != 0) {
+		cpuinfo_isa.jscvt = true;
+	} else {
+		// Mandatory in ARMv8.3-A, list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.jscvt = true;
+		}
+	}
+
+	const uint32_t has_feat_dotprod = get_sys_info_by_name("hw.optional.arm.FEAT_DotProd");
+	if (has_feat_dotprod != 0) {
+		cpuinfo_isa.dot = true;
+	} else {
+		// Mandatory in ARMv8.4-A, list only cores released before iOS 15 / macOS 12
+		switch (cpu_family) {
+			case CPUFAMILY_ARM_LIGHTNING_THUNDER:
+			case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
+				cpuinfo_isa.dot = true;
+		}
+	}
+
+	const uint32_t has_feat_i8mm = get_sys_info_by_name("hw.optional.arm.FEAT_I8MM");
+	if (has_feat_i8mm != 0) {
+		cpuinfo_isa.i8mm = true;
 	}
 
 	uint32_t num_clusters = 1;
@@ -374,7 +391,7 @@
 			.core_id = i % cores_per_package,
 			.package = packages + i / cores_per_package,
 			.vendor = cpuinfo_vendor_apple,
-			.uarch = decode_uarch(cpu_family, cpu_subtype, i, mach_topology.cores),
+			.uarch = decode_uarch(cpu_family, i, mach_topology.cores),
 		};
 		if (i != 0 && cores[i].uarch != cores[i - 1].uarch) {
 			num_clusters++;
diff --git a/src/arm/midr.h b/src/arm/midr.h
index 739dc19..b0e244c 100644
--- a/src/arm/midr.h
+++ b/src/arm/midr.h
@@ -174,23 +174,25 @@
 		case UINT32_C(0x53000030): /* Exynos M4 */
 		case UINT32_C(0x53000040): /* Exynos M5 */
 		case UINT32_C(0x4100D440): /* Cortex-X1 */
-			/* These cores are in big role w.r.t Cortex-A75/-A76/-A77/-A78 */
+		case UINT32_C(0x4100D480): /* Cortex-X2 */
+			/* These cores are in big role w.r.t Cortex-A75/-A76/-A77/-A78/-A710 */
 			return 6;
-		case UINT32_C(0x4E000030): /* Denver 2 */
-		case UINT32_C(0x53000010): /* Exynos M1 and Exynos M2 */
-		case UINT32_C(0x53000020): /* Exynos M3 */
-		case UINT32_C(0x51008040): /* Kryo 485 Gold / Gold Prime */
-		case UINT32_C(0x51008020): /* Kryo 385 Gold */
-		case UINT32_C(0x51008000): /* Kryo 260 / 280 Gold */
-		case UINT32_C(0x51002050): /* Kryo Gold */
-		case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
-		case UINT32_C(0x4100D410): /* Cortex-A78 */
+		case UINT32_C(0x4100D080): /* Cortex-A72 */
+		case UINT32_C(0x4100D090): /* Cortex-A73 */
+		case UINT32_C(0x4100D0A0): /* Cortex-A75 */
+		case UINT32_C(0x4100D0B0): /* Cortex-A76 */
 		case UINT32_C(0x4100D0D0): /* Cortex-A77 */
 		case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
-		case UINT32_C(0x4100D0B0): /* Cortex-A76 */
-		case UINT32_C(0x4100D0A0): /* Cortex-A75 */
-		case UINT32_C(0x4100D090): /* Cortex-A73 */
-		case UINT32_C(0x4100D080): /* Cortex-A72 */
+		case UINT32_C(0x4100D410): /* Cortex-A78 */
+		case UINT32_C(0x4100D470): /* Cortex-A710 */
+		case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
+		case UINT32_C(0x4E000030): /* Denver 2 */
+		case UINT32_C(0x51002050): /* Kryo Gold */
+		case UINT32_C(0x51008000): /* Kryo 260 / 280 Gold */
+		case UINT32_C(0x51008020): /* Kryo 385 Gold */
+		case UINT32_C(0x51008040): /* Kryo 485 Gold / Gold Prime */
+		case UINT32_C(0x53000010): /* Exynos M1 and Exynos M2 */
+		case UINT32_C(0x53000020): /* Exynos M3 */
 #if CPUINFO_ARCH_ARM
 		case UINT32_C(0x4100C0F0): /* Cortex-A15 */
 		case UINT32_C(0x4100C0E0): /* Cortex-A17 */
@@ -205,8 +207,9 @@
 #if CPUINFO_ARCH_ARM64
 		case UINT32_C(0x4100D060): /* Cortex-A65 */
 #endif /* CPUINFO_ARCH_ARM64 */
-		case UINT32_C(0x4100D050): /* Cortex-A55 */
 		case UINT32_C(0x4100D030): /* Cortex-A53 */
+		case UINT32_C(0x4100D050): /* Cortex-A55 */
+		case UINT32_C(0x4100D460): /* Cortex-A510 */
 			/* Cortex-A53 is usually in LITTLE role, but can be in big role w.r.t. Cortex-A35 */
 			return 2;
 		case UINT32_C(0x4100D040): /* Cortex-A35 */
diff --git a/src/arm/uarch.c b/src/arm/uarch.c
index 8b5362b..1d4c6ee 100644
--- a/src/arm/uarch.c
+++ b/src/arm/uarch.c
@@ -91,13 +91,30 @@
 				case 0xD0E: /* Cortex-A76AE */
 					*uarch = cpuinfo_uarch_cortex_a76;
 					break;
+#if CPUINFO_ARCH_ARM64 && !defined(__ANDROID__)
+				case 0xD40:
+					*uarch = cpuinfo_uarch_neoverse_v1;
+					break;
+#endif /* CPUINFO_ARCH_ARM64 && !defined(__ANDROID__) */
 				case 0xD41: /* Cortex-A78 */
 					*uarch = cpuinfo_uarch_cortex_a78;
 					break;
 				case 0xD44: /* Cortex-X1 */
 					*uarch = cpuinfo_uarch_cortex_x1;
 					break;
+				case 0xD46: /* Cortex-A510 */
+					*uarch = cpuinfo_uarch_cortex_a510;
+					break;
+				case 0xD47: /* Cortex-A710 */
+					*uarch = cpuinfo_uarch_cortex_a710;
+					break;
+				case 0xD48: /* Cortex-X2 */
+					*uarch = cpuinfo_uarch_cortex_x2;
+					break;
 #if CPUINFO_ARCH_ARM64 && !defined(__ANDROID__)
+				case 0xD49:
+					*uarch = cpuinfo_uarch_neoverse_n2;
+					break;
 				case 0xD4A:
 					*uarch = cpuinfo_uarch_neoverse_e1;
 					break;
diff --git a/src/arm/windows/init-by-logical-sys-info.c b/src/arm/windows/init-by-logical-sys-info.c
new file mode 100644
index 0000000..f088011
--- /dev/null
+++ b/src/arm/windows/init-by-logical-sys-info.c
@@ -0,0 +1,885 @@
+#include <stdio.h>

+#include <stdint.h>

+#include <stdlib.h>

+#include <string.h>

+#include <malloc.h>

+#include <errno.h>

+#include <sys/types.h>

+

+#include <cpuinfo.h>

+#include <cpuinfo/internal-api.h>

+#include <cpuinfo/log.h>

+

+#include "windows-arm-init.h"

+

+#define MAX_NR_OF_CACHES	(cpuinfo_cache_level_max - 1)

+

+/* Call chain:

+ * cpu_info_init_by_logical_sys_info

+ * 		read_packages_for_processors

+ * 		read_cores_for_processors

+ * 		read_caches_for_processors

+ * 			read_all_logical_processor_info_of_relation

+ * 				parse_relation_processor_info

+ * 					store_package_info_per_processor

+ * 					store_core_info_per_processor

+ * 				parse_relation_cache_info

+ * 					store_cache_info_per_processor

+ */

+

+static uint32_t count_logical_processors(

+	const uint32_t max_group_count,

+	uint32_t* global_proc_index_per_group);

+

+static uint32_t read_packages_for_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info *chip_info);

+

+static uint32_t read_cores_for_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info);

+

+static uint32_t read_caches_for_processors(

+	struct cpuinfo_processor *processors,

+	const uint32_t number_of_processors,

+	struct cpuinfo_cache *caches,

+	uint32_t* numbers_of_caches,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info *chip_info);

+

+static uint32_t read_all_logical_processor_info_of_relation(

+	LOGICAL_PROCESSOR_RELATIONSHIP info_type,

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	struct cpuinfo_cache* caches,

+	uint32_t* numbers_of_caches,

+	struct cpuinfo_core* cores,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info *chip_info);

+

+static bool parse_relation_processor_info(

+	struct cpuinfo_processor* processors,

+	uint32_t nr_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,

+	const uint32_t info_id,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info);

+

+static bool parse_relation_cache_info(

+	struct cpuinfo_processor* processors,

+	struct cpuinfo_cache* caches,

+	uint32_t* numbers_of_caches,

+	const uint32_t* global_proc_index_per_group,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info);

+

+static void store_package_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	const uint32_t package_id,

+	const uint32_t group_id,

+	const uint32_t processor_id_in_group);

+

+static void store_core_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	const uint32_t core_id,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info);

+

+static void store_cache_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,

+	struct cpuinfo_cache* current_cache);

+

+static bool connect_packages_cores_clusters_by_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t nr_of_processors,

+	struct cpuinfo_package* packages,

+	const uint32_t nr_of_packages,

+	struct cpuinfo_cluster* clusters,

+	struct cpuinfo_core* cores,

+	const uint32_t nr_of_cores,

+	const struct woa_chip_info* chip_info,

+	enum cpuinfo_vendor vendor);

+

+static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity);

+

+

+bool cpu_info_init_by_logical_sys_info(

+	const struct woa_chip_info *chip_info,

+	const enum cpuinfo_vendor vendor)

+{

+	struct cpuinfo_processor* processors = NULL;

+	struct cpuinfo_package* packages = NULL;

+	struct cpuinfo_cluster* clusters = NULL;

+	struct cpuinfo_core* cores = NULL;

+	struct cpuinfo_cache* caches = NULL;

+	struct cpuinfo_uarch_info* uarchs = NULL;

+

+	uint32_t nr_of_packages = 0;

+	uint32_t nr_of_cores = 0;

+	uint32_t nr_of_all_caches = 0;

+	uint32_t numbers_of_caches[MAX_NR_OF_CACHES] = {0};

+	

+	uint32_t nr_of_uarchs = 0;

+	bool result = false;

+	

+	HANDLE heap = GetProcessHeap();

+

+	/* 1. Count available logical processor groups and processors */

+	const uint32_t max_group_count = (uint32_t) GetMaximumProcessorGroupCount();

+	cpuinfo_log_debug("detected %"PRIu32" processor group(s)", max_group_count);

+	/* We need to store the absolute processor ID offsets for every groups, because

+	 *  1. We can't assume every processor groups include the same number of

+	 *     logical processors.

+	 *  2. Every processor groups know its group number and processor IDs within

+	 *     the group, but not the global processor IDs.

+	 *  3. We need to list every logical processors by global IDs.

+	*/

+	uint32_t* global_proc_index_per_group =

+		(uint32_t*) HeapAlloc(heap, 0, max_group_count * sizeof(uint32_t));

+	if (global_proc_index_per_group == NULL) {

+		cpuinfo_log_error(

+			"failed to allocate %zu bytes for descriptions of %"PRIu32" processor groups",

+			max_group_count * sizeof(struct cpuinfo_processor), max_group_count);

+		goto clean_up;

+	}

+	

+	uint32_t nr_of_processors =

+		count_logical_processors(max_group_count, global_proc_index_per_group);

+	processors = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_processors * sizeof(struct cpuinfo_processor));

+	if (processors == NULL) {

+		cpuinfo_log_error(

+			"failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors",

+			nr_of_processors * sizeof(struct cpuinfo_processor), nr_of_processors);

+		goto clean_up;

+	}

+

+	/* 2. Read topology information via MSDN API: packages, cores and caches*/

+	nr_of_packages = read_packages_for_processors(

+						processors, nr_of_processors,

+						global_proc_index_per_group,

+						chip_info);

+	if (!nr_of_packages) {

+		cpuinfo_log_error("error in reading package information");

+		goto clean_up;

+	}

+	cpuinfo_log_debug("detected %"PRIu32" processor package(s)", nr_of_packages);

+

+	/* We need the EfficiencyClass to parse uarch from the core information,

+	 * but we need to iterate first to count cores and allocate memory then

+	 * we will iterate again to read and store data to cpuinfo_core structures.

+	 */

+	nr_of_cores = read_cores_for_processors(

+					processors, nr_of_processors,

+					global_proc_index_per_group, NULL,

+					chip_info);

+	if (!nr_of_cores) {

+		cpuinfo_log_error("error in reading core information");

+		goto clean_up;

+	}

+	cpuinfo_log_debug("detected %"PRIu32" processor core(s)", nr_of_cores);

+

+	/* There is no API to read number of caches, so we need to iterate twice on caches:

+		1. Count all type of caches -> allocate memory

+		2. Read out cache data and store to allocated memory

+	 */

+	nr_of_all_caches = read_caches_for_processors(

+						processors, nr_of_processors,

+						caches, numbers_of_caches,

+						global_proc_index_per_group, chip_info);

+	if (!nr_of_all_caches) {

+		cpuinfo_log_error("error in reading cache information");

+		goto clean_up;

+	}

+	cpuinfo_log_debug("detected %"PRIu32" processor cache(s)", nr_of_all_caches);

+

+	/* 3. Allocate memory for package, cluster, core and cache structures */

+	packages = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_packages * sizeof(struct cpuinfo_package));

+	if (packages == NULL) {

+		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" physical packages",

+			nr_of_packages * sizeof(struct cpuinfo_package), nr_of_packages);

+		goto clean_up;

+	}

+

+	/* We don't have cluster information so we explicitly set clusters to equal to cores. */

+	clusters = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_cluster));

+	if (clusters == NULL) {

+		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters",

+			nr_of_cores * sizeof(struct cpuinfo_cluster), nr_of_cores);

+		goto clean_up;

+	}

+

+	cores = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_core));

+	if (cores == NULL) {

+		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores",

+			nr_of_cores * sizeof(struct cpuinfo_core), nr_of_cores);

+		goto clean_up;

+	}

+

+	/* We allocate one contiguous cache array for all caches, then use offsets per cache type. */

+	caches = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_all_caches * sizeof(struct cpuinfo_cache));

+	if (caches == NULL) {

+		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" caches",

+			nr_of_all_caches * sizeof(struct cpuinfo_cache), nr_of_all_caches);

+		goto clean_up;

+	}

+

+	/* 4.Read missing topology information that can't be saved without counted

+	 *   allocate structures in the first round.

+	 */

+	nr_of_all_caches = read_caches_for_processors(

+						processors, nr_of_processors,

+						caches, numbers_of_caches, global_proc_index_per_group, chip_info);

+	if (!nr_of_all_caches) {

+		cpuinfo_log_error("error in reading cache information");

+		goto clean_up;

+	}

+

+	nr_of_cores = read_cores_for_processors(

+		processors, nr_of_processors,

+		global_proc_index_per_group, cores,

+		chip_info);

+	if (!nr_of_cores) {

+		cpuinfo_log_error("error in reading core information");

+		goto clean_up;

+	}

+

+	/* 5. Now that we read out everything from the system we can, fill the package, cluster

+	 *    and core structures respectively.

+	 */

+	result = connect_packages_cores_clusters_by_processors(

+				processors, nr_of_processors,

+				packages, nr_of_packages,

+				clusters,

+				cores, nr_of_cores,

+				chip_info,

+				vendor);

+	if(!result) {

+		cpuinfo_log_error("error in connecting information");

+		goto clean_up;

+	}

+

+	/* 6. Count and store uarchs of cores, assuming same uarchs are neighbors */

+	enum cpuinfo_uarch prev_uarch = cpuinfo_uarch_unknown;

+	for (uint32_t i = 0; i < nr_of_cores; i++) {

+		if (prev_uarch != cores[i].uarch) {

+			nr_of_uarchs++;

+			prev_uarch = cores[i].uarch;

+		}

+	}

+	uarchs = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_uarchs * sizeof(struct cpuinfo_uarch_info));

+	if (uarchs == NULL) {

+		cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" uarchs",

+			nr_of_uarchs * sizeof(struct cpuinfo_uarch_info), nr_of_uarchs);

+		goto clean_up;

+	}

+	prev_uarch = cpuinfo_uarch_unknown;

+	for (uint32_t i = 0, uarch_counter = 0; i < nr_of_cores; i++) {

+		if (prev_uarch != cores[i].uarch) {

+			prev_uarch = cores[i].uarch;

+			uarchs[uarch_counter].uarch = cores[i].uarch;

+			uarchs[uarch_counter].core_count = 1;

+			uarchs[uarch_counter].processor_count = cores[i].processor_count;

+			uarch_counter++;

+		} else if (prev_uarch != cpuinfo_uarch_unknown) {

+			uarchs[uarch_counter].core_count++;

+			uarchs[uarch_counter].processor_count += cores[i].processor_count;

+		}

+	}

+

+	/* 7. Commit changes */

+	cpuinfo_processors = processors;

+	cpuinfo_packages = packages;

+	cpuinfo_clusters = clusters;

+	cpuinfo_cores = cores;

+	cpuinfo_uarchs = uarchs;

+

+	cpuinfo_processors_count = nr_of_processors;

+	cpuinfo_packages_count = nr_of_packages;

+	cpuinfo_clusters_count = nr_of_cores;

+	cpuinfo_cores_count = nr_of_cores;

+	cpuinfo_uarchs_count = nr_of_uarchs;

+

+	for (uint32_t i = 0; i < MAX_NR_OF_CACHES; i++) {

+		cpuinfo_cache_count[i] = numbers_of_caches[i];

+	}

+	cpuinfo_cache[cpuinfo_cache_level_1i] = caches;

+	cpuinfo_cache[cpuinfo_cache_level_1d] = cpuinfo_cache[cpuinfo_cache_level_1i] + cpuinfo_cache_count[cpuinfo_cache_level_1i];

+	cpuinfo_cache[cpuinfo_cache_level_2]  = cpuinfo_cache[cpuinfo_cache_level_1d] + cpuinfo_cache_count[cpuinfo_cache_level_1d];

+	cpuinfo_cache[cpuinfo_cache_level_3]  = cpuinfo_cache[cpuinfo_cache_level_2]  + cpuinfo_cache_count[cpuinfo_cache_level_2];

+	cpuinfo_cache[cpuinfo_cache_level_4]  = cpuinfo_cache[cpuinfo_cache_level_3]  + cpuinfo_cache_count[cpuinfo_cache_level_3];

+	cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);

+

+	result = true;

+	MemoryBarrier();

+

+	processors = NULL;

+	packages = NULL;

+	clusters = NULL;

+	cores = NULL;

+	caches = NULL;

+	uarchs = NULL;

+

+clean_up:

+	/* The propagated pointers, shouldn't be freed, only in case of error

+	 * and unfinished init.

+	 */

+	if (processors != NULL) {

+		HeapFree(heap, 0, processors);

+	}

+	if (packages != NULL) {

+		HeapFree(heap, 0, packages);

+	}

+	if (clusters != NULL) {

+		HeapFree(heap, 0, clusters);

+	}

+	if (cores != NULL) {

+		HeapFree(heap, 0, cores);

+	}

+	if (caches != NULL) {

+		HeapFree(heap, 0, caches);

+	}

+	if (uarchs != NULL) {

+		HeapFree(heap, 0, uarchs);

+	}

+

+	/* Free the locally used temporary pointers */

+	HeapFree(heap, 0, global_proc_index_per_group);

+	global_proc_index_per_group = NULL;

+	return result;

+}

+

+static uint32_t count_logical_processors(

+	const uint32_t max_group_count,

+	uint32_t* global_proc_index_per_group)

+{

+	uint32_t nr_of_processors = 0;

+

+	for (uint32_t i = 0; i < max_group_count; i++) {

+		uint32_t nr_of_processors_per_group = GetMaximumProcessorCount((WORD) i);

+		cpuinfo_log_debug("detected %"PRIu32" processor(s) in group %"PRIu32"",

+			nr_of_processors_per_group, i);

+		global_proc_index_per_group[i] = nr_of_processors;

+		nr_of_processors += nr_of_processors_per_group;

+	}

+	return nr_of_processors;

+}

+

+static uint32_t read_packages_for_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info *chip_info)

+{

+	return read_all_logical_processor_info_of_relation(

+		RelationProcessorPackage,

+		processors,

+		number_of_processors,

+		NULL,

+		NULL,

+		NULL,

+		global_proc_index_per_group,

+		chip_info);

+}

+

+uint32_t read_cores_for_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info)

+{

+	return read_all_logical_processor_info_of_relation(

+		RelationProcessorCore,

+		processors,

+		number_of_processors,

+		NULL,

+		NULL,

+		cores,

+		global_proc_index_per_group,

+		chip_info);

+}

+

+static uint32_t read_caches_for_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	struct cpuinfo_cache* caches,

+	uint32_t* numbers_of_caches,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info *chip_info)

+{

+	/* Reset processor start indexes */

+	if (caches) {

+		uint32_t cache_offset = 0;

+		for (uint32_t i = 0; i < MAX_NR_OF_CACHES; i++) {

+			for (uint32_t j = 0; j < numbers_of_caches[i]; j++) {

+				caches[cache_offset + j].processor_start = UINT32_MAX;

+			}

+			cache_offset += numbers_of_caches[i];

+		}

+	}

+

+	return read_all_logical_processor_info_of_relation(

+		RelationCache,

+		processors,

+		number_of_processors,

+		caches,

+		numbers_of_caches,

+		NULL,

+		global_proc_index_per_group,

+		chip_info);

+}

+

+static uint32_t read_all_logical_processor_info_of_relation(

+	LOGICAL_PROCESSOR_RELATIONSHIP info_type,

+	struct cpuinfo_processor* processors,

+	const uint32_t number_of_processors,

+	struct cpuinfo_cache* caches,

+	uint32_t* numbers_of_caches,

+	struct cpuinfo_core* cores,

+	const uint32_t* global_proc_index_per_group,

+	const struct woa_chip_info* chip_info)

+{

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX infos = NULL;

+	uint32_t nr_of_structs = 0;

+	DWORD info_size = 0;

+	bool result = false;

+	HANDLE heap = GetProcessHeap();

+

+	/* 1. Query the size of the information structure first */

+	if (GetLogicalProcessorInformationEx(info_type, NULL, &info_size) == FALSE) {

+		const DWORD last_error = GetLastError();

+		if (last_error != ERROR_INSUFFICIENT_BUFFER) {

+			cpuinfo_log_error(

+				"failed to query size of processor %"PRIu32" information information: error %"PRIu32"",

+				(uint32_t)info_type, (uint32_t) last_error);

+			goto clean_up;

+		}

+	}

+	/* 2. Allocate memory for the information structure */

+	infos = HeapAlloc(heap, 0, info_size);

+	if (infos == NULL) {

+		cpuinfo_log_error("failed to allocate %"PRIu32" bytes for logical processor information",

+			(uint32_t) info_size);

+		goto clean_up;

+	}

+	/* 3. Read the information structure */

+	if (GetLogicalProcessorInformationEx(info_type, infos, &info_size) == FALSE) {

+		cpuinfo_log_error("failed to query processor %"PRIu32" information: error %"PRIu32"",

+			(uint32_t)info_type, (uint32_t) GetLastError());

+		goto clean_up;

+	}

+

+	/* 4. Parse the structure and store relevant data */

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info_end =

+		(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) infos + info_size);

+	for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = infos;

+		info < info_end;

+		info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) info + info->Size))

+	{

+		if (info->Relationship != info_type) {

+			cpuinfo_log_warning(

+				"unexpected processor info type (%"PRIu32") for processor information",

+				(uint32_t) info->Relationship);

+			continue;

+		}

+

+		const uint32_t info_id = nr_of_structs++;

+

+		switch(info_type) {

+			case RelationProcessorPackage:

+				result = parse_relation_processor_info(

+							processors,

+							number_of_processors,

+							global_proc_index_per_group,

+							info,

+							info_id,

+							cores,

+							chip_info);

+			break;

+			case RelationProcessorCore:

+				result = parse_relation_processor_info(

+							processors,

+							number_of_processors,

+							global_proc_index_per_group,

+							info,

+							info_id,

+							cores,

+							chip_info);

+			break;

+			case RelationCache:

+				result = parse_relation_cache_info(

+							processors,

+							caches,

+							numbers_of_caches,

+							global_proc_index_per_group,

+							info);

+			break;

+			default:

+				cpuinfo_log_error(

+					"unexpected processor info type (%"PRIu32") for processor information",

+					(uint32_t) info->Relationship);

+				result = false;

+			break;

+		}

+		if (!result) {

+			nr_of_structs = 0;

+			goto clean_up;

+		}

+	}

+clean_up:

+	/* 5. Release dynamically allocated info structure. */

+	HeapFree(heap, 0, infos);

+	infos = NULL;

+	return nr_of_structs;

+}

+

+static bool parse_relation_processor_info(

+	struct cpuinfo_processor* processors,

+	uint32_t nr_of_processors,

+	const uint32_t* global_proc_index_per_group,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,

+	const uint32_t info_id,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info)

+{

+	for (uint32_t i = 0; i < info->Processor.GroupCount; i++) {

+		const uint32_t group_id = info->Processor.GroupMask[i].Group;

+		/* Bitmask representing processors in this group belonging to this package */

+		KAFFINITY group_processors_mask = info->Processor.GroupMask[i].Mask;

+		while (group_processors_mask != 0) {

+			const uint32_t processor_id_in_group =

+				low_index_from_kaffinity(group_processors_mask);

+			const uint32_t processor_global_index =

+				global_proc_index_per_group[group_id] + processor_id_in_group;

+

+			if(processor_global_index >= nr_of_processors) {

+				cpuinfo_log_error("unexpected processor index %"PRIu32"",

+					processor_global_index);

+				return false;

+			}

+

+			switch(info->Relationship) {

+				case RelationProcessorPackage:

+					store_package_info_per_processor(

+						processors, processor_global_index, info_id,

+						group_id, processor_id_in_group);

+				break;

+				case RelationProcessorCore:

+					store_core_info_per_processor(

+						processors, processor_global_index,

+						info_id, info,

+						cores, chip_info);

+				break;

+				default:

+					cpuinfo_log_error(

+						"unexpected processor info type (%"PRIu32") for processor information",

+						(uint32_t) info->Relationship);

+				break;

+			}

+			/* Clear the bits in affinity mask, lower the least set bit. */

+			group_processors_mask &= (group_processors_mask - 1);

+		}

+	}

+	return true;

+}

+

+static bool parse_relation_cache_info(

+	struct cpuinfo_processor* processors,

+	struct cpuinfo_cache* caches,

+	uint32_t* numbers_of_caches,

+	const uint32_t* global_proc_index_per_group,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info)

+{

+	static uint32_t l1i_counter = 0;

+	static uint32_t l1d_counter = 0;

+	static uint32_t l2_counter = 0;

+	static uint32_t l3_counter = 0;

+

+	/* Count cache types for allocation at first. */

+	if (caches == NULL) {

+		switch(info->Cache.Level) {

+			case 1:

+				switch (info->Cache.Type) {

+					case CacheInstruction:

+						numbers_of_caches[cpuinfo_cache_level_1i]++;

+					break;

+					case CacheData:

+						numbers_of_caches[cpuinfo_cache_level_1d]++;

+					break;

+					case CacheUnified:

+					break;

+					case CacheTrace:

+					break;

+					default:

+					break;

+				}

+			break;

+			case 2:

+				numbers_of_caches[cpuinfo_cache_level_2]++;

+			break;

+			case 3:

+				numbers_of_caches[cpuinfo_cache_level_3]++;

+			break;

+		}

+		return true;

+	}

+	struct cpuinfo_cache* l1i_base = caches;

+	struct cpuinfo_cache* l1d_base = l1i_base + numbers_of_caches[cpuinfo_cache_level_1i];

+	struct cpuinfo_cache* l2_base  = l1d_base + numbers_of_caches[cpuinfo_cache_level_1d];

+	struct cpuinfo_cache* l3_base  = l2_base  + numbers_of_caches[cpuinfo_cache_level_2];

+

+	cpuinfo_log_debug(

+		"info->Cache.GroupCount:%"PRIu32", info->Cache.GroupMask:%"PRIu32","

+		"info->Cache.Level:%"PRIu32", info->Cache.Associativity:%"PRIu32","

+		"info->Cache.LineSize:%"PRIu32","

+		"info->Cache.CacheSize:%"PRIu32", info->Cache.Type:%"PRIu32"",

+		info->Cache.GroupCount, (unsigned int)info->Cache.GroupMask.Mask,

+		info->Cache.Level, info->Cache.Associativity, info->Cache.LineSize,

+		info->Cache.CacheSize, info->Cache.Type);

+

+	struct cpuinfo_cache* current_cache = NULL;

+	switch (info->Cache.Level) {

+		case 1:

+			switch (info->Cache.Type) {

+				case CacheInstruction:

+					current_cache = l1i_base + l1i_counter;

+					l1i_counter++;

+				break;

+				case CacheData:

+					current_cache = l1d_base + l1d_counter;

+					l1d_counter++;

+				break;

+				case CacheUnified:

+				break;

+				case CacheTrace:

+				break;

+				default:

+				break;

+			}

+		break;

+		case 2:

+			current_cache = l2_base + l2_counter;

+			l2_counter++;

+		break;

+		case 3:

+			current_cache = l3_base + l3_counter;

+			l3_counter++;

+		break;

+	}

+	current_cache->size = info->Cache.CacheSize;

+	current_cache->line_size = info->Cache.LineSize;

+	current_cache->associativity = info->Cache.Associativity;

+	/* We don't have partition and set information of caches on Windows,

+	 * so we set partitions to 1 and calculate the expected sets.

+	 */

+	current_cache->partitions = 1;

+	current_cache->sets =

+		current_cache->size / current_cache->line_size / current_cache->associativity;

+	if (info->Cache.Type == CacheUnified) {

+		current_cache->flags = CPUINFO_CACHE_UNIFIED;

+	}

+

+	for (uint32_t i = 0; i <= info->Cache.GroupCount; i++) {

+	/* Zero GroupCount is valid, GroupMask still can store bits set. */

+		const uint32_t group_id = info->Cache.GroupMasks[i].Group;

+		/* Bitmask representing processors in this group belonging to this package */

+		KAFFINITY group_processors_mask = info->Cache.GroupMasks[i].Mask;

+		while (group_processors_mask != 0) {

+			const uint32_t processor_id_in_group =

+				low_index_from_kaffinity(group_processors_mask);

+			const uint32_t processor_global_index =

+				global_proc_index_per_group[group_id] + processor_id_in_group;

+

+			store_cache_info_per_processor(

+				processors, processor_global_index,

+				info, current_cache);

+

+			/* Clear the bits in affinity mask, lower the least set bit. */

+			group_processors_mask &= (group_processors_mask - 1);

+		}

+	}

+	return true;

+}

+

+static void store_package_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	const uint32_t package_id,

+	const uint32_t group_id,

+	const uint32_t processor_id_in_group)

+{

+	processors[processor_global_index].windows_group_id =

+		(uint16_t) group_id;

+	processors[processor_global_index].windows_processor_id =

+		(uint16_t) processor_id_in_group;

+

+	/* As we're counting the number of packages now, we haven't allocated memory for

+	 * cpuinfo_packages yet, so we only set the package pointer's offset now.

+	 */

+	processors[processor_global_index].package =

+		(const struct cpuinfo_package*) NULL + package_id;

+}

+

+void store_core_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	const uint32_t core_id,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info,

+	struct cpuinfo_core* cores,

+	const struct woa_chip_info *chip_info)

+{

+	if (cores) {

+		processors[processor_global_index].core = cores + core_id;

+		cores[core_id].core_id = core_id;

+		get_core_uarch_for_efficiency(

+			chip_info->chip_name, core_info->Processor.EfficiencyClass,

+			&(cores[core_id].uarch), &(cores[core_id].frequency));

+

+		/* We don't have cluster information, so we handle it as

+		 * fixed 1 to (cluster / cores).

+		 * Set the cluster offset ID now, as soon as we have the

+		 * cluster base address, we'll set the absolute address.

+		 */

+		processors[processor_global_index].cluster =

+			(const struct cpuinfo_cluster*) NULL + core_id;

+	}

+}

+

+static void store_cache_info_per_processor(

+	struct cpuinfo_processor* processors,

+	const uint32_t processor_global_index,

+	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,

+	struct cpuinfo_cache* current_cache)

+{

+	if (current_cache->processor_start > processor_global_index) {

+		current_cache->processor_start = processor_global_index;

+	}

+	current_cache->processor_count++;

+

+	switch(info->Cache.Level) {

+		case 1:

+			switch (info->Cache.Type) {

+				case CacheInstruction:

+					processors[processor_global_index].cache.l1i = current_cache;

+				break;

+				case CacheData:

+					processors[processor_global_index].cache.l1d = current_cache;

+				break;

+				case CacheUnified:

+				break;

+				case CacheTrace:

+				break;

+				default:

+				break;

+			}

+		break;

+		case 2:

+			processors[processor_global_index].cache.l2 = current_cache;

+		break;

+		case 3:

+			processors[processor_global_index].cache.l3 = current_cache;

+		break;

+	}

+}

+

+static bool connect_packages_cores_clusters_by_processors(

+	struct cpuinfo_processor* processors,

+	const uint32_t nr_of_processors,

+	struct cpuinfo_package* packages,

+	const uint32_t nr_of_packages,

+	struct cpuinfo_cluster* clusters,

+	struct cpuinfo_core* cores,

+	const uint32_t nr_of_cores,

+	const struct woa_chip_info* chip_info,

+	enum cpuinfo_vendor vendor)

+{

+	/* Adjust core and package pointers for all logical processors. */

+	for (uint32_t i = nr_of_processors; i != 0; i--) {

+		const uint32_t processor_id = i - 1;

+		struct cpuinfo_processor* processor = processors + processor_id;

+

+		struct cpuinfo_core* core = (struct cpuinfo_core*)processor->core;

+

+		/* We stored the offset of pointers when we haven't allocated memory

+		 * for packages and clusters, so now add offsets to base addresses.

+		 */

+		struct cpuinfo_package* package =

+			(struct cpuinfo_package*) ((uintptr_t) packages + (uintptr_t) processor->package);

+		if (package < packages ||

+			package >= (packages + nr_of_packages)) {

+			cpuinfo_log_error("invalid package indexing");

+			return false;

+		}

+		processor->package = package;

+

+		struct cpuinfo_cluster* cluster =

+			(struct cpuinfo_cluster*) ((uintptr_t) clusters + (uintptr_t) processor->cluster);

+		if (cluster < clusters ||

+			cluster >= (clusters + nr_of_cores)) {

+			cpuinfo_log_error("invalid cluster indexing");

+			return false;

+		}

+		processor->cluster = cluster;

+

+		if (chip_info) {

+			strncpy_s(package->name, CPUINFO_PACKAGE_NAME_MAX, chip_info->chip_name_string,

+				strnlen(chip_info->chip_name_string, CPUINFO_PACKAGE_NAME_MAX));

+		}

+

+		/* Set start indexes and counts per packages / clusters / cores - going backwards */

+

+		/* This can be overwritten by lower-index processors on the same package. */

+		package->processor_start = processor_id;

+		package->processor_count++;

+

+		/* This can be overwritten by lower-index processors on the same cluster. */

+		cluster->processor_start = processor_id;

+		cluster->processor_count++;

+

+		/* This can be overwritten by lower-index processors on the same core. */

+		core->processor_start = processor_id;

+		core->processor_count++;

+	}

+	/* Fill cores */

+	for (uint32_t i = nr_of_cores; i != 0; i--) {

+		const uint32_t global_core_id = i - 1;

+		struct cpuinfo_core* core = cores + global_core_id;

+		const struct cpuinfo_processor* processor = processors + core->processor_start;

+		struct cpuinfo_package* package = (struct cpuinfo_package*) processor->package;

+		struct cpuinfo_cluster* cluster = (struct cpuinfo_cluster*) processor->cluster;

+

+		core->package = package;

+		core->cluster = cluster;

+		core->vendor = vendor;

+

+		/* This can be overwritten by lower-index cores on the same cluster/package. */

+		cluster->core_start = global_core_id;

+		cluster->core_count++;

+		package->core_start = global_core_id;

+		package->core_count++;

+		package->cluster_start = global_core_id;

+		package->cluster_count = package->core_count;

+

+		cluster->package = package;

+		cluster->vendor = cores[cluster->core_start].vendor;

+		cluster->uarch = cores[cluster->core_start].uarch;

+		cluster->frequency = cores[cluster->core_start].frequency;

+	}

+	return true;

+}

+

+static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity) {

+	unsigned long index;

+	_BitScanForward64(&index, (unsigned __int64) kaffinity);

+	return (uint32_t) index;

+}

diff --git a/src/arm/windows/init.c b/src/arm/windows/init.c
new file mode 100644
index 0000000..8effc15
--- /dev/null
+++ b/src/arm/windows/init.c
@@ -0,0 +1,253 @@
+#include <stdio.h>

+#include <stdint.h>

+#include <stdlib.h>

+#include <errno.h>

+

+#include <cpuinfo.h>

+#include <cpuinfo/internal-api.h>

+#include <cpuinfo/log.h>

+

+#include "windows-arm-init.h"

+

+/* Efficiency class = 0 means little core, while 1 means big core for now */

+#define MAX_WOA_VALID_EFFICIENCY_CLASSES		2

+#define VENDOR_NAME_MAX		CPUINFO_PACKAGE_NAME_MAX

+

+struct cpuinfo_arm_isa cpuinfo_isa;

+

+static void set_cpuinfo_isa_fields(void);

+static bool get_system_info_from_registry(

+	struct woa_chip_info** chip_info,

+	enum cpuinfo_vendor* vendor);

+

+struct vendor_info {

+	char vendor_name[VENDOR_NAME_MAX];

+	enum cpuinfo_vendor vendor;

+};

+

+/* Please add new vendor here! */

+static struct vendor_info vendors[] = {

+	{

+		"Qualcomm",

+		cpuinfo_vendor_qualcomm

+	}

+};

+

+/* Please add new SoC/chip info here! */

+static struct woa_chip_info woa_chips[] = {

+	/* Microsoft SQ1 Kryo 495 4 + 4 cores (3 GHz + 1.80 GHz) */

+	{

+		"Microsoft SQ1",

+		woa_chip_name_microsoft_sq_1,

+		{

+			{

+				cpuinfo_uarch_cortex_a55,

+				1800000000,

+			},

+			{

+				cpuinfo_uarch_cortex_a76,

+				3000000000,

+			}

+		}

+	},

+	/* Microsoft SQ2 Kryo 495 4 + 4 cores (3.15 GHz + 2.42 GHz) */

+	{

+		"Microsoft SQ2",

+		woa_chip_name_microsoft_sq_2,

+		{

+			{

+				cpuinfo_uarch_cortex_a55,

+				2420000000,

+			},

+			{

+				cpuinfo_uarch_cortex_a76,

+				3150000000

+			}

+		}

+	}

+};

+

+BOOL CALLBACK cpuinfo_arm_windows_init(

+	PINIT_ONCE init_once, PVOID parameter, PVOID* context)

+{

+	struct woa_chip_info *chip_info = NULL;

+	enum cpuinfo_vendor vendor = cpuinfo_vendor_unknown;

+	bool result = false;

+	

+	set_cpuinfo_isa_fields();

+	result = get_system_info_from_registry(&chip_info, &vendor);	

+	result &= cpu_info_init_by_logical_sys_info(chip_info, vendor);

+	cpuinfo_is_initialized = result;

+	return ((result == true) ? TRUE : FALSE);

+}

+

+bool get_core_uarch_for_efficiency(

+	enum woa_chip_name chip, BYTE EfficiencyClass,

+	enum cpuinfo_uarch* uarch, uint64_t* frequency)

+{

+	/* For currently supported WoA chips, the Efficiency class selects

+	 * the pre-defined little and big core.

+	 * Any further supported SoC's logic should be implemented here.

+	 */

+	if (uarch && frequency && chip < woa_chip_name_last &&

+		EfficiencyClass < MAX_WOA_VALID_EFFICIENCY_CLASSES) {

+		*uarch = woa_chips[chip].uarchs[EfficiencyClass].uarch;

+		*frequency = woa_chips[chip].uarchs[EfficiencyClass].frequency;

+		return true;

+	}

+	return false;

+}

+

+/* Static helper functions */

+

+static bool read_registry(

+	LPCTSTR subkey,

+	LPCTSTR value,

+	char** textBuffer)

+{

+	DWORD keyType = 0;

+	DWORD dataSize = 0;

+	const DWORD flags = RRF_RT_REG_SZ; /* Only read strings (REG_SZ) */

+	LSTATUS result = 0;

+	HANDLE heap = GetProcessHeap();

+

+	result = RegGetValue(

+		HKEY_LOCAL_MACHINE, 

+		subkey,

+		value,

+		flags,

+		&keyType,

+		NULL, /* Request buffer size */

+		&dataSize);

+	if (result != 0 || dataSize == 0) {

+		cpuinfo_log_error("Registry entry size read error");

+		return false;

+	}

+

+	if (*textBuffer) {

+		HeapFree(heap, 0, *textBuffer);

+	}

+	*textBuffer = HeapAlloc(heap, HEAP_ZERO_MEMORY, dataSize);

+	if (*textBuffer == NULL) {

+		cpuinfo_log_error("Registry textbuffer allocation error");

+		return false;

+	}

+

+	result = RegGetValue(

+		HKEY_LOCAL_MACHINE,

+		subkey,

+		value,

+		flags,

+		NULL,

+		*textBuffer, /* Write string in this destination buffer */

+		&dataSize);

+	if (result != 0) {

+		cpuinfo_log_error("Registry read error");

+		return false;

+	}

+	return true;

+}

+

+static bool get_system_info_from_registry(

+	struct woa_chip_info** chip_info,

+	enum cpuinfo_vendor* vendor)

+{

+	bool result = false;

+	char* textBuffer = NULL;

+	LPCTSTR cpu0_subkey =

+		(LPCTSTR)"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";

+	LPCTSTR chip_name_value = (LPCTSTR)"ProcessorNameString";

+	LPCTSTR vendor_name_value = (LPCTSTR)"VendorIdentifier";

+

+	*chip_info = NULL;

+	*vendor = cpuinfo_vendor_unknown;

+	HANDLE heap = GetProcessHeap();

+

+	/* 1. Read processor model name from registry and find in the hard-coded list. */

+	if (!read_registry(cpu0_subkey, chip_name_value, &textBuffer)) {

+		cpuinfo_log_error("Registry read error");

+		goto cleanup;

+	}

+	for (uint32_t i = 0; i < (uint32_t) woa_chip_name_last; i++) {

+		size_t compare_length = strnlen(woa_chips[i].chip_name_string, CPUINFO_PACKAGE_NAME_MAX);

+		int compare_result = strncmp(textBuffer, woa_chips[i].chip_name_string, compare_length);

+		if (compare_result == 0) {

+			*chip_info = woa_chips+i;

+			break;

+		}

+	}

+	if (*chip_info == NULL) {

+		cpuinfo_log_error("Unknown chip model name.\n Please add new Windows on Arm SoC/chip support!");

+		goto cleanup;

+	}

+	cpuinfo_log_debug("detected chip model name: %s", (**chip_info).chip_name_string);

+

+	/* 2. Read vendor/manufacturer name from registry. */

+	if (!read_registry(cpu0_subkey, vendor_name_value, &textBuffer)) {

+		cpuinfo_log_error("Registry read error");

+		goto cleanup;

+	}

+

+	for (uint32_t i = 0; i < (sizeof(vendors) / sizeof(struct vendor_info)); i++) {

+		if (strncmp(textBuffer, vendors[i].vendor_name,

+				strlen(vendors[i].vendor_name)) == 0) {

+			*vendor = vendors[i].vendor;

+			result = true;

+			break;

+		}

+	}

+	if (*vendor == cpuinfo_vendor_unknown) {

+		cpuinfo_log_error("Unexpected vendor: %s", textBuffer);

+	}

+

+cleanup:

+	HeapFree(heap, 0, textBuffer);

+	textBuffer = NULL;

+	return result;

+}

+

+static void set_cpuinfo_isa_fields(void)

+{

+	bool armv8 = IsProcessorFeaturePresent(PF_ARM_V8_INSTRUCTIONS_AVAILABLE);

+	bool crypto = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);

+	bool load_store_atomic = IsProcessorFeaturePresent(PF_ARM_64BIT_LOADSTORE_ATOMIC);

+	bool float_multiply_accumulate = IsProcessorFeaturePresent(PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE);

+	bool crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE);

+	bool float_emulated = IsProcessorFeaturePresent(PF_FLOATING_POINT_EMULATED);

+

+	/* Read all Arm related Windows features for debug purposes, even if we can't

+	 * pair Arm ISA feature to that now.

+	 */

+#if CPUINFO_LOG_DEBUG_PARSERS

+	bool divide = IsProcessorFeaturePresent(PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE);

+	bool ext_cache = IsProcessorFeaturePresent(PF_ARM_EXTERNAL_CACHE_AVAILABLE);

+	bool vfp_registers = IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE);

+	bool arm_v81 = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE);

+

+	cpuinfo_log_debug("divide present: %d", divide);

+	cpuinfo_log_debug("ext_cache present: %d", ext_cache);

+	cpuinfo_log_debug("vfp_registers present: %d", vfp_registers);

+	cpuinfo_log_debug("arm_v81 present: %d", arm_v81);

+#endif

+

+	cpuinfo_log_debug("armv8 present: %d", armv8);

+	cpuinfo_log_debug("crypto present: %d", crypto);

+	cpuinfo_log_debug("load_store_atomic present: %d", load_store_atomic);

+	cpuinfo_log_debug("float_multiply_accumulate present: %d", float_multiply_accumulate);

+	cpuinfo_log_debug("crc32 present: %d", crc32);

+	cpuinfo_log_debug("float_emulated: %d", float_emulated);

+

+#if CPUINFO_ARCH_ARM

+	cpuinfo_isa.armv8 = armv8;

+#endif

+#if CPUINFO_ARCH_ARM64

+	cpuinfo_isa.atomics = load_store_atomic;

+#endif

+	cpuinfo_isa.crc32 = crc32;

+	/* Windows API reports all or nothing for cryptographic instructions. */

+	cpuinfo_isa.aes = crypto;

+	cpuinfo_isa.sha1 = crypto;

+	cpuinfo_isa.sha2 = crypto;

+	cpuinfo_isa.pmull = crypto;

+	cpuinfo_isa.fp16arith = !float_emulated && float_multiply_accumulate;

+}

diff --git a/src/arm/windows/windows-arm-init.h b/src/arm/windows/windows-arm-init.h
new file mode 100644
index 0000000..76cc51e
--- /dev/null
+++ b/src/arm/windows/windows-arm-init.h
@@ -0,0 +1,32 @@
+#pragma once

+

+/* List of known and supported Windows on Arm SoCs/chips. */

+enum woa_chip_name {

+	woa_chip_name_microsoft_sq_1 = 0,

+	woa_chip_name_microsoft_sq_2 = 1,

+	woa_chip_name_unknown = 2,

+	woa_chip_name_last = woa_chip_name_unknown

+};

+

+/* Topology information hard-coded by SoC/chip name */

+struct core_info_by_chip_name {

+	enum cpuinfo_uarch uarch;

+	uint64_t frequency; /* Hz */

+};

+

+/* SoC/chip info that's currently not readable by logical system information,

+ * but can be read from registry.

+ */

+struct woa_chip_info {

+	char* chip_name_string;

+	enum woa_chip_name chip_name;

+	struct core_info_by_chip_name uarchs[woa_chip_name_last];

+};

+

+bool get_core_uarch_for_efficiency(

+	enum woa_chip_name chip, BYTE EfficiencyClass,

+	enum cpuinfo_uarch* uarch, uint64_t* frequency);

+

+bool cpu_info_init_by_logical_sys_info(

+	const struct woa_chip_info *chip_info,

+	enum cpuinfo_vendor vendor);

diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h
index 9c23d7c..c04620e 100644
--- a/src/cpuinfo/internal-api.h
+++ b/src/cpuinfo/internal-api.h
@@ -51,7 +51,11 @@
 CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void);
 CPUINFO_PRIVATE void cpuinfo_x86_linux_init(void);
 #if defined(_WIN32) || defined(__CYGWIN__)
-	CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context);
+	#if CPUINFO_ARCH_ARM64
+		CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context);
+	#else
+		CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context);
+	#endif
 #endif
 CPUINFO_PRIVATE void cpuinfo_arm_mach_init(void);
 CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void);
diff --git a/src/init.c b/src/init.c
index d61e7be..ed37c07 100644
--- a/src/init.c
+++ b/src/init.c
@@ -37,6 +37,8 @@
 		pthread_once(&init_guard, &cpuinfo_arm_linux_init);
 	#elif defined(__MACH__) && defined(__APPLE__)
 		pthread_once(&init_guard, &cpuinfo_arm_mach_init);
+	#elif defined(_WIN32)
+		InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL);
 	#else
 		cpuinfo_log_error("operating system is not supported in cpuinfo");
 	#endif
diff --git a/src/x86/name.c b/src/x86/name.c
index a7cc7c6..957a0d8 100644
--- a/src/x86/name.c
+++ b/src/x86/name.c
@@ -234,7 +234,7 @@
 				return true;
 			}
 			/*
-			 * Erase everywhing after "SOC" on AMD System-on-Chips, e.g.
+			 * Erase everything after "SOC" on AMD System-on-Chips, e.g.
 			 *  "AMD GX-212JC SOC with Radeon(TM) R2E Graphics  \0"
 			 */
 			if (erase_matching(token_start, token_length, "SOC")) {
diff --git a/test/arm-cache.cc b/test/arm-cache.cc
index 4d6218b..6711373 100644
--- a/test/arm-cache.cc
+++ b/test/arm-cache.cc
@@ -1684,7 +1684,7 @@
 	EXPECT_EQ(16 * 1024, l1i.size);
 	EXPECT_EQ(16 * 1024, l1d.size);
 	EXPECT_EQ(0, l2.size);
-	EXPECT_EQ(0, big_l3.size);
+	EXPECT_EQ(0, l3.size);
 }
 
 TEST(BROADCOM, bcm2836) {
@@ -1706,7 +1706,7 @@
 	EXPECT_EQ(32 * 1024, l1i.size);
 	EXPECT_EQ(32 * 1024, l1d.size);
 	EXPECT_EQ(512 * 1024, l2.size);
-	EXPECT_EQ(0, big_l3.size);
+	EXPECT_EQ(0, l3.size);
 }
 
 TEST(BROADCOM, bcm2837) {
@@ -1728,7 +1728,7 @@
 	EXPECT_EQ(16 * 1024, l1i.size);
 	EXPECT_EQ(16 * 1024, l1d.size);
 	EXPECT_EQ(512 * 1024, l2.size);
-	EXPECT_EQ(0, big_l3.size);
+	EXPECT_EQ(0, l3.size);
 }
 
 TEST(BROADCOM, bcm2711) {
@@ -1750,5 +1750,5 @@
 	EXPECT_EQ(48 * 1024, l1i.size);
 	EXPECT_EQ(32 * 1024, l1d.size);
 	EXPECT_EQ(1024 * 1024, l2.size);
-	EXPECT_EQ(0, big_l3.size);
+	EXPECT_EQ(0, l3.size);
 }
diff --git a/tools/cpu-info.c b/tools/cpu-info.c
index 30ec633..8602fc0 100644
--- a/tools/cpu-info.c
+++ b/tools/cpu-info.c
@@ -187,8 +187,20 @@
 			return "Cortex-A77";
 		case cpuinfo_uarch_cortex_a78:
 			return "Cortex-A78";
+		case cpuinfo_uarch_cortex_a510:
+			return "Cortex-A510";
+		case cpuinfo_uarch_cortex_a710:
+			return "Cortex-A710";
 		case cpuinfo_uarch_cortex_x1:
 			return "Cortex-X1";
+		case cpuinfo_uarch_cortex_x2:
+			return "Cortex-X2";
+		case cpuinfo_uarch_neoverse_n1:
+			return "Neoverse-N1";
+		case cpuinfo_uarch_neoverse_v1:
+			return "Neoverse-V1";
+		case cpuinfo_uarch_neoverse_n2:
+			return "Neoverse-N2";
 		case cpuinfo_uarch_scorpion:
 			return "Scorpion";
 		case cpuinfo_uarch_krait:
@@ -241,6 +253,10 @@
 			return "Firestorm";
 		case cpuinfo_uarch_icestorm:
 			return "Icestorm";
+		case cpuinfo_uarch_avalanche:
+			return "Avalanche";
+		case cpuinfo_uarch_blizzard:
+			return "Blizzard";
 		case cpuinfo_uarch_thunderx:
 			return "ThunderX";
 		case cpuinfo_uarch_thunderx2:
diff --git a/tools/isa-info.c b/tools/isa-info.c
index 92abb57..4ac9c8b 100644
--- a/tools/isa-info.c
+++ b/tools/isa-info.c
@@ -143,7 +143,8 @@
 		printf("\tNEON VQRDMLAH/VQRDMLSH: %s\n", cpuinfo_has_arm_neon_rdm() ? "yes" : "no");
 		printf("\tNEON FP16 arithmetics: %s\n", cpuinfo_has_arm_neon_fp16_arith() ? "yes" : "no");
 		printf("\tNEON complex: %s\n", cpuinfo_has_arm_fcma() ? "yes" : "no");
-		printf("\tNEON dot product: %s\n", cpuinfo_has_arm_neon_dot() ? "yes" : "no");
+		printf("\tNEON VSDOT/VUDOT: %s\n", cpuinfo_has_arm_neon_dot() ? "yes" : "no");
+		printf("\tNEON VFMLAL/VFMLSL: %s\n", cpuinfo_has_arm_fhm() ? "yes" : "no");
 
 	printf("Cryptography extensions:\n");
 		printf("\tAES: %s\n", cpuinfo_has_arm_aes() ? "yes" : "no");
@@ -157,7 +158,10 @@
 		printf("\tARM v8.1 atomics: %s\n", cpuinfo_has_arm_atomics() ? "yes" : "no");
 		printf("\tARM v8.1 SQRDMLxH: %s\n", cpuinfo_has_arm_neon_rdm() ? "yes" : "no");
 		printf("\tARM v8.2 FP16 arithmetics: %s\n", cpuinfo_has_arm_fp16_arith() ? "yes" : "no");
-		printf("\tARM v8.3 dot product: %s\n", cpuinfo_has_arm_neon_dot() ? "yes" : "no");
+		printf("\tARM v8.2 FHM: %s\n", cpuinfo_has_arm_fhm() ? "yes" : "no");
+		printf("\tARM v8.2 BF16: %s\n", cpuinfo_has_arm_bf16() ? "yes" : "no");
+		printf("\tARM v8.2 Int8 dot product: %s\n", cpuinfo_has_arm_neon_dot() ? "yes" : "no");
+		printf("\tARM v8.2 Int8 matrix multiplication: %s\n", cpuinfo_has_arm_i8mm() ? "yes" : "no");
 		printf("\tARM v8.3 JS conversion: %s\n", cpuinfo_has_arm_jscvt() ? "yes" : "no");
 		printf("\tARM v8.3 complex: %s\n", cpuinfo_has_arm_fcma() ? "yes" : "no");