Merge v0.7.0 into master.

Adjust Android build system rules for recent changes in upstream
cpu_features.

Test: Run `atest -a --test-mapping external/cpu_features:all` (on
      x86-64 and Arm64 devices)
Change-Id: Idd78a3a4f29247b2c83117bde98cca45f94908f2
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..716b782
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,31 @@
+# Project Files unneeded by docker
+ci/Makefile
+ci/docker
+ci/doc
+ci/cache
+.git
+.gitignore
+.github
+.dockerignore
+.clang-format
+appveyor.yml
+.travis.yml
+AUTHORS
+CONTRIBUTING.md
+CONTRIBUTORS
+LICENSE
+README.md
+
+build/
+cmake_build/
+build_cross/
+cmake-build-*/
+out/
+
+# Editor directories and files
+.idea/
+.vagrant/
+.vscode/
+.vs/
+*.user
+*.swp
diff --git a/.github/workflows/Dockerfile b/.github/workflows/Dockerfile
index 41dfc93..8d6f3dc 100644
--- a/.github/workflows/Dockerfile
+++ b/.github/workflows/Dockerfile
@@ -2,4 +2,4 @@
 # ref: https://hub.docker.com/_/alpine
 FROM alpine:edge
 # Install system build dependencies
-RUN apk add --no-cache git clang
+RUN apk add --no-cache git clang-extra-tools
diff --git a/.github/workflows/aarch64_linux.yml b/.github/workflows/aarch64_linux.yml
new file mode 100644
index 0000000..2de7289
--- /dev/null
+++ b/.github/workflows/aarch64_linux.yml
@@ -0,0 +1,28 @@
+name: aarch64 Linux
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  aarch64:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        targets: [
+          [aarch64-linux-gnu],
+          [aarch64_be-linux-gnu]
+        ]
+      fail-fast: false
+    env:
+      TARGET: ${{ matrix.targets[0] }}
+    steps:
+    - uses: actions/checkout@v2
+    - name: Build
+      run: make --directory=ci ${TARGET}_build
+    - name: Test
+      run: make --directory=ci ${TARGET}_test
diff --git a/.github/workflows/amd64_freebsd.yml b/.github/workflows/amd64_freebsd.yml
new file mode 100644
index 0000000..eeab380
--- /dev/null
+++ b/.github/workflows/amd64_freebsd.yml
@@ -0,0 +1,22 @@
+name: amd64 FreeBSD
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Only MacOS hosted runner provides virtualisation with vagrant/virtualbox installed.
+  # see: https://github.com/actions/virtual-environments/tree/main/images/macos
+  freebsd:
+    runs-on: macos-10.15
+    steps:
+    - uses: actions/checkout@v2
+    - name: vagrant version
+      run: Vagrant --version
+    - name: VirtualBox version
+      run: virtualbox -h
+    - name: Build
+      run: cd ci/vagrant/freebsd && vagrant up
diff --git a/.github/workflows/amd64_linux.yml b/.github/workflows/amd64_linux.yml
new file mode 100644
index 0000000..21f4f90
--- /dev/null
+++ b/.github/workflows/amd64_linux.yml
@@ -0,0 +1,31 @@
+name: amd64 Linux
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  make:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Env
+      run: make --directory=ci amd64_env
+    - name: Devel
+      run: make --directory=ci amd64_devel
+    - name: Build
+      run: make --directory=ci amd64_build
+    - name: Test
+      run: make --directory=ci amd64_test
+    - name: Install Env
+      run: make --directory=ci amd64_install_env
+    - name: Install Devel
+      run: make --directory=ci amd64_install_devel
+    - name: Install Build
+      run: make --directory=ci amd64_install_build
+    - name: Install Test
+      run: make --directory=ci amd64_install_test
diff --git a/.github/workflows/amd64_macos.yml b/.github/workflows/amd64_macos.yml
new file mode 100644
index 0000000..19ec18c
--- /dev/null
+++ b/.github/workflows/amd64_macos.yml
@@ -0,0 +1,43 @@
+name: amd64 macOS
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  xcode:
+    runs-on: macos-latest
+    env:
+      CTEST_OUTPUT_ON_FAILURE: 1
+    steps:
+    - uses: actions/checkout@v2
+    - name: Check cmake
+      run: cmake --version
+    - name: Configure
+      run: cmake -S. -Bbuild -G "Xcode" -DCMAKE_CONFIGURATION_TYPES=Release
+    - name: Build
+      run: cmake --build build --config Release --target ALL_BUILD -v
+    - name: Test
+      run: cmake --build build --config Release --target RUN_TESTS -v
+    - name: Install
+      run: cmake --build build --config Release --target install -v
+  make:
+    runs-on: macos-latest
+    env:
+      CTEST_OUTPUT_ON_FAILURE: 1
+    steps:
+    - uses: actions/checkout@v2
+    - name: Check cmake
+      run: cmake --version
+    - name: Configure
+      run: cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release
+    - name: Build
+      run: cmake --build build --target all -v
+    - name: Test
+      run: cmake --build build --target test -v
+    - name: Install
+      run: cmake --build build --target install -v
diff --git a/.github/workflows/amd64_windows.yml b/.github/workflows/amd64_windows.yml
new file mode 100644
index 0000000..7ff1ac0
--- /dev/null
+++ b/.github/workflows/amd64_windows.yml
@@ -0,0 +1,25 @@
+name: amd64 Windows
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  msvc:
+    runs-on: windows-latest
+    env:
+      CTEST_OUTPUT_ON_FAILURE: 1
+    steps:
+    - uses: actions/checkout@v2
+    - name: Configure
+      run: cmake -S. -Bbuild -G "Visual Studio 17 2022" -DCMAKE_CONFIGURATION_TYPES=Release
+    - name: Build
+      run: cmake --build build --config Release --target ALL_BUILD -- /maxcpucount
+    - name: Test
+      run: cmake --build build --config Release --target RUN_TESTS -- /maxcpucount
+    - name: Install
+      run: cmake --build build --config Release --target INSTALL -- /maxcpucount
diff --git a/.github/workflows/arm_linux.yml b/.github/workflows/arm_linux.yml
new file mode 100644
index 0000000..2272188
--- /dev/null
+++ b/.github/workflows/arm_linux.yml
@@ -0,0 +1,31 @@
+name: arm Linux
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  arm:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        targets: [
+          [arm-linux-gnueabihf],
+          [armv8l-linux-gnueabihf],
+          [arm-linux-gnueabi],
+          [armeb-linux-gnueabihf],
+          [armeb-linux-gnueabi]
+        ]
+      fail-fast: false
+    env:
+      TARGET: ${{ matrix.targets[0] }}
+    steps:
+    - uses: actions/checkout@v2
+    - name: Build
+      run: make --directory=ci ${TARGET}_build
+    - name: Test
+      run: make --directory=ci ${TARGET}_test
diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml
index 17d1567..1afd391 100644
--- a/.github/workflows/clang_format.yml
+++ b/.github/workflows/clang_format.yml
@@ -8,8 +8,8 @@
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - name: Fetch origin/master
-      run: git fetch origin master
+    - name: Fetch origin/main
+      run: git fetch origin main
     - name: List of changed file(s)
       run: git diff --name-only FETCH_HEAD
 
diff --git a/.github/workflows/mips_linux.yml b/.github/workflows/mips_linux.yml
new file mode 100644
index 0000000..571de3a
--- /dev/null
+++ b/.github/workflows/mips_linux.yml
@@ -0,0 +1,30 @@
+name: mips Linux
+
+on:
+  push:
+  pull_request:
+  schedule:
+    # min hours day(month) month day(week)
+    - cron: '0 0 7,22 * *'
+
+jobs:
+  # Building using the github runner environement directly.
+  mips:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        targets: [
+          [mips32],
+          [mips32el],
+          [mips64],
+          [mips64el]
+        ]
+      fail-fast: false
+    env:
+      TARGET: ${{ matrix.targets[0] }}
+    steps:
+    - uses: actions/checkout@v2
+    - name: Build
+      run: make --directory=ci ${TARGET}_build
+    - name: Test
+      run: make --directory=ci ${TARGET}_test
diff --git a/.gitignore b/.gitignore
index 6285424..f918154 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,19 @@
-cmake_build/
+# Build folders
 build/
+cmake_build/
+build_cross/
+cmake-build-*/
+out/
 
+# IDEs / CI temp files
+.idea/
+.vagrant/
+.vscode/
+.vs/
 *.swp
+
+# Bazel artifacts
+**/bazel-*
+
+# Per-user bazelrc files
+user.bazelrc
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b5845be..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-language: c
-
-sudo: false
-
-cache:
-  timeout: 1000
-  directories:
-    - $HOME/cpu_features_archives
-
-addons:
-  apt_packages:
-    - ninja-build
-
-env:
-  global:
-    TOOLCHAIN=NATIVE
-    CMAKE_GENERATOR=Ninja
-
-matrix:
-  include:
-  - os: linux
-    compiler: gcc
-    env:
-      TARGET=x86_64-linux-gnu
-  - os: linux
-    compiler: clang
-    env:
-      TARGET=x86_64-linux-gnu
-  - os: osx
-    compiler: gcc
-    env:
-      TARGET=x86_64-osx
-      CMAKE_GENERATOR="Unix Makefiles"
-  - os: osx
-    compiler: clang
-    env:
-      TARGET=x86_64-osx
-      CMAKE_GENERATOR="Unix Makefiles"
-  - os: windows
-    env:
-      TARGET=x86_64-windows
-      CMAKE_GENERATOR="Visual Studio 15 2017 Win64"
-
-  # see: https://docs.travis-ci.com/user/multi-cpu-architectures/
-  - os: linux
-    arch: ppc64le
-    compiler: gcc
-    env:
-      TARGET=ppc64le-linux-gnu
-  - os: linux
-    arch: ppc64le
-    compiler: clang
-    env:
-      TARGET=ppc64le-linux-gnu
-
-  # Toolchains for little-endian, 64-bit ARMv8 for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=aarch64-linux-gnu
-      QEMU_ARCH=aarch64
-  # Toolchains for little-endian, hard-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=arm-linux-gnueabihf
-      QEMU_ARCH=arm
-  # Toolchains for little-endian, 32-bit ARMv8 for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=armv8l-linux-gnueabihf
-      QEMU_ARCH=arm
-  # Toolchains for little-endian, soft-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=arm-linux-gnueabi
-      QEMU_ARCH=arm
-  # Toolchains for big-endian, 64-bit ARMv8 for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=aarch64_be-linux-gnu
-      QEMU_ARCH=DISABLED
-  # Toolchains for big-endian, hard-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=armeb-linux-gnueabihf
-      QEMU_ARCH=DISABLED
-  # Toolchains for big-endian, soft-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
-  - os: linux
-    env:
-      TOOLCHAIN=LINARO
-      TARGET=armeb-linux-gnueabi
-      QEMU_ARCH=DISABLED
-  - os: linux
-    env:
-      TOOLCHAIN=CODESCAPE
-      TARGET=mips32
-      QEMU_ARCH=mips
-  - os: linux
-    env:
-      TOOLCHAIN=CODESCAPE
-      TARGET=mips32el
-      QEMU_ARCH=mipsel
-  - os: linux
-    env:
-      TOOLCHAIN=CODESCAPE
-      TARGET=mips64
-      QEMU_ARCH=mips64
-  - os: linux
-    env:
-      TOOLCHAIN=CODESCAPE
-      TARGET=mips64el
-      QEMU_ARCH=mips64el
-
-script:
-  - cmake --version
-  - bash -e -x ./scripts/run_integration.sh
diff --git a/Android.bp b/Android.bp
index 7f70060..57e3804 100644
--- a/Android.bp
+++ b/Android.bp
@@ -101,7 +101,7 @@
     arch: {
         arm: {
             srcs: [
-                "src/cpuinfo_arm.c",
+                "src/impl_arm_linux_or_android.c",
             ],
             whole_static_libs: [
                 "libcpu_features-hwcaps",
@@ -109,7 +109,7 @@
         },
         arm64: {
             srcs: [
-                "src/cpuinfo_aarch64.c",
+                "src/impl_aarch64_linux_or_android.c",
             ],
             whole_static_libs: [
                 "libcpu_features-hwcaps",
@@ -120,7 +120,7 @@
         },
         x86: {
             srcs: [
-                "src/cpuinfo_x86.c",
+                "src/impl_x86_linux_or_android.c",
             ],
             cflags: [
                 "-Wno-unused-variable",
@@ -128,7 +128,7 @@
         },
         x86_64: {
             srcs: [
-                "src/cpuinfo_x86.c",
+                "src/impl_x86_linux_or_android.c",
             ],
             cflags: [
                 "-Wno-unused-variable",
@@ -171,6 +171,16 @@
                 "-Wno-unused-function",
             ],
         },
+        x86: {
+            cflags: [
+                "-Wno-deprecated-declarations",
+            ],
+        },
+        x86_64: {
+            cflags: [
+                "-Wno-deprecated-declarations",
+            ],
+        },
     },
 }
 
@@ -334,26 +344,28 @@
             cflags: [
                 "-DCPU_FEATURES_MOCK_CPUID_X86",
                 "-Wno-unused-variable",
+                "-Wno-deprecated-declarations",
             ],
             srcs: [
                 "test/cpuinfo_x86_test.cc",
-                "src/cpuinfo_x86.c",
+                "src/impl_x86_linux_or_android.c",
             ],
         },
         x86_64: {
             cflags: [
                 "-DCPU_FEATURES_MOCK_CPUID_X86",
                 "-Wno-unused-variable",
+                "-Wno-deprecated-declarations",
             ],
             srcs: [
                 "test/cpuinfo_x86_test.cc",
-                "src/cpuinfo_x86.c",
+                "src/impl_x86_linux_or_android.c",
             ],
         },
         arm: {
             srcs: [
                 "test/cpuinfo_arm_test.cc",
-                "src/cpuinfo_arm.c",
+                "src/impl_arm_linux_or_android.c",
             ],
         },
         arm64: {
@@ -362,7 +374,7 @@
             ],
             srcs: [
                 "test/cpuinfo_aarch64_test.cc",
-                "src/cpuinfo_aarch64.c",
+                "src/impl_aarch64_linux_or_android.c",
             ],
         },
     },
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 0000000..1b62d66
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,330 @@
+# cpu_features, a cross platform C99 library to get cpu features at runtime.
+
+load("@bazel_skylib//lib:selects.bzl", "selects")
+load("//:bazel/platforms.bzl", "PLATFORM_CPU_ARM", "PLATFORM_CPU_ARM64", "PLATFORM_CPU_MIPS", "PLATFORM_CPU_PPC", "PLATFORM_CPU_X86_64")
+
+package(
+    default_visibility = ["//visibility:public"],
+    licenses = ["notice"],
+)
+
+exports_files(["LICENSE"])
+
+INCLUDES = ["include"]
+
+C99_FLAGS = [
+    "-std=c99",
+    "-Wall",
+    "-Wextra",
+    "-Wmissing-declarations",
+    "-Wmissing-prototypes",
+    "-Wno-implicit-fallthrough",
+    "-Wno-unused-function",
+    "-Wold-style-definition",
+    "-Wshadow",
+    "-Wsign-compare",
+    "-Wstrict-prototypes",
+]
+
+cc_library(
+    name = "cpu_features_macros",
+    hdrs = ["include/cpu_features_macros.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+)
+
+cc_library(
+    name = "cpu_features_cache_info",
+    hdrs = ["include/cpu_features_cache_info.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    deps = [":cpu_features_macros"],
+)
+
+cc_library(
+    name = "bit_utils",
+    hdrs = ["include/internal/bit_utils.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    deps = [":cpu_features_macros"],
+)
+
+cc_test(
+    name = "bit_utils_test",
+    srcs = ["test/bit_utils_test.cc"],
+    includes = INCLUDES,
+    deps = [
+        ":bit_utils",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "memory_utils",
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    textual_hdrs = [
+        "src/copy.inl",
+        "src/equals.inl",
+    ],
+)
+
+cc_library(
+    name = "string_view",
+    srcs = [
+        "src/string_view.c",
+    ],
+    hdrs = ["include/internal/string_view.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    deps = [
+        ":cpu_features_macros",
+        ":memory_utils",
+    ],
+)
+
+cc_test(
+    name = "string_view_test",
+    srcs = ["test/string_view_test.cc"],
+    includes = INCLUDES,
+    deps = [
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "filesystem",
+    srcs = ["src/filesystem.c"],
+    hdrs = ["include/internal/filesystem.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    deps = [":cpu_features_macros"],
+)
+
+cc_library(
+    name = "filesystem_for_testing",
+    testonly = 1,
+    srcs = [
+        "src/filesystem.c",
+        "test/filesystem_for_testing.cc",
+    ],
+    hdrs = [
+        "include/internal/filesystem.h",
+        "test/filesystem_for_testing.h",
+    ],
+    includes = INCLUDES,
+    defines = ["CPU_FEATURES_MOCK_FILESYSTEM"],
+    deps = [
+        ":cpu_features_macros",
+    ],
+)
+
+cc_library(
+    name = "stack_line_reader",
+    srcs = ["src/stack_line_reader.c"],
+    hdrs = ["include/internal/stack_line_reader.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "stack_line_reader_test",
+    srcs = [
+        "include/internal/stack_line_reader.h",
+        "src/stack_line_reader.c",
+        "test/stack_line_reader_test.cc",
+    ],
+    includes = INCLUDES,
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=16"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_library(
+    name = "stack_line_reader_to_use_with_filesystem_for_testing",
+    testonly = 1,
+    srcs = ["src/stack_line_reader.c"],
+    hdrs = ["include/internal/stack_line_reader.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":string_view",
+    ],
+)
+
+cc_library(
+    name = "hwcaps",
+    srcs = ["src/hwcaps.c"],
+    hdrs = ["include/internal/hwcaps.h"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    defines = ["HAVE_STRONG_GETAUXVAL"],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem",
+        ":string_view",
+    ],
+)
+
+cc_library(
+    name = "hwcaps_for_testing",
+    testonly = 1,
+    srcs = [
+        "src/hwcaps.c",
+        "test/hwcaps_for_testing.cc",
+    ],
+    hdrs = [
+        "include/internal/hwcaps.h",
+        "test/hwcaps_for_testing.h",
+    ],
+    includes = INCLUDES,
+    defines = [
+        "CPU_FEATURES_MOCK_GET_ELF_HWCAP_FROM_GETAUXVAL",
+        "CPU_FEATURES_TEST",
+    ],
+    deps = [
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":string_view",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo",
+    srcs = selects.with_or({
+        PLATFORM_CPU_X86_64: [
+            "src/impl_x86_freebsd.c",
+            "src/impl_x86_linux_or_android.c",
+            "src/impl_x86_macos.c",
+            "src/impl_x86_windows.c",
+        ],
+        PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
+        PLATFORM_CPU_ARM64: ["src/impl_aarch64_linux_or_android.c"],
+        PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
+        PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
+    }),
+    hdrs = selects.with_or({
+        PLATFORM_CPU_X86_64: [
+            "include/cpuinfo_x86.h",
+            "include/internal/cpuid_x86.h",
+        ],
+        PLATFORM_CPU_ARM: ["include/cpuinfo_arm.h"],
+        PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
+        PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
+        PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
+    }),
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    textual_hdrs = selects.with_or({
+        PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
+        "//conditions:default": [],
+    }) + [
+        "src/define_introspection.inl",
+        "src/define_introspection_and_hwcaps.inl",
+    ],
+    deps = [
+        ":bit_utils",
+        ":cpu_features_cache_info",
+        ":cpu_features_macros",
+        ":filesystem",
+        ":hwcaps",
+        ":memory_utils",
+        ":stack_line_reader",
+        ":string_view",
+    ],
+)
+
+cc_library(
+    name = "cpuinfo_for_testing",
+    testonly = 1,
+    srcs = selects.with_or({
+        PLATFORM_CPU_X86_64: [
+            "src/impl_x86_freebsd.c",
+            "src/impl_x86_linux_or_android.c",
+            "src/impl_x86_macos.c",
+            "src/impl_x86_windows.c",
+        ],
+        PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
+        PLATFORM_CPU_ARM64: ["src/impl_aarch64_linux_or_android.c"],
+        PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
+        PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
+    }),
+    hdrs = selects.with_or({
+        PLATFORM_CPU_X86_64: [
+            "include/cpuinfo_x86.h",
+            "include/internal/cpuid_x86.h",
+        ],
+        PLATFORM_CPU_ARM: ["include/cpuinfo_arm.h"],
+        PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
+        PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
+        PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
+    }),
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    defines = selects.with_or({
+        PLATFORM_CPU_X86_64: ["CPU_FEATURES_MOCK_CPUID_X86"],
+        "//conditions:default": [],
+    }),
+    textual_hdrs = selects.with_or({
+        PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
+        "//conditions:default": [],
+    }) + [
+        "src/define_introspection.inl",
+        "src/define_introspection_and_hwcaps.inl",
+    ],
+    deps = [
+        ":bit_utils",
+        ":cpu_features_cache_info",
+        ":cpu_features_macros",
+        ":filesystem_for_testing",
+        ":hwcaps_for_testing",
+        ":memory_utils",
+        ":stack_line_reader_to_use_with_filesystem_for_testing",
+        ":string_view",
+    ],
+)
+
+cc_test(
+    name = "cpuinfo_test",
+    srcs = selects.with_or({
+        PLATFORM_CPU_ARM64: ["test/cpuinfo_aarch64_test.cc"],
+        PLATFORM_CPU_ARM: ["test/cpuinfo_arm_test.cc"],
+        PLATFORM_CPU_MIPS: ["test/cpuinfo_mips_test.cc"],
+        PLATFORM_CPU_PPC: ["test/cpuinfo_ppc_test.cc"],
+        PLATFORM_CPU_X86_64: ["test/cpuinfo_x86_test.cc"],
+    }),
+    includes = INCLUDES,
+    deps = [
+        ":cpuinfo_for_testing",
+        ":filesystem_for_testing",
+        ":hwcaps_for_testing",
+        ":string_view",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
+cc_binary(
+    name = "list_cpu_features",
+    srcs = ["src/utils/list_cpu_features.c"],
+    copts = C99_FLAGS,
+    includes = INCLUDES,
+    deps = [
+        ":bit_utils",
+        ":cpu_features_macros",
+        ":cpuinfo",
+    ],
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f9daeac..81451d4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@
   cmake_policy(SET CMP0077 NEW)
 endif()
 
-project(CpuFeatures VERSION 0.6.0 LANGUAGES C)
+project(CpuFeatures VERSION 0.7.0 LANGUAGES C)
 
 set(CMAKE_C_STANDARD 99)
 
@@ -17,22 +17,17 @@
       FORCE)
 endif(NOT CMAKE_BUILD_TYPE)
 
-# BUILD_TESTING is a standard CMake variable, but we declare it here to make it
-# prominent in the GUI.
-option(BUILD_TESTING "Enable test (depends on googletest)." OFF)
 # BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
 # it prominent in the GUI.
 # cpu_features uses bit-fields which are - to some extends - implementation-defined (see https://en.cppreference.com/w/c/language/bit_field).
 # As a consequence it is discouraged to use cpu_features as a shared library because different compilers may interpret the code in different ways.
 # Prefer static linking from source whenever possible.
 option(BUILD_SHARED_LIBS "Build library as shared." OFF)
-# PIC
-option(BUILD_PIC "Build with Position Independant Code." OFF) # Default is off at least for GCC
 
 # Force PIC on unix when building shared libs
 # see: https://en.wikipedia.org/wiki/Position-independent_code
 if(BUILD_SHARED_LIBS AND UNIX)
-  set(BUILD_PIC ON)
+  option(CMAKE_POSITION_INDEPENDENT_CODE "Build with Position Independant Code." ON)
 endif()
 
 include(CheckIncludeFile)
@@ -57,10 +52,10 @@
 
 if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
   set(PROCESSOR_IS_MIPS TRUE)
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)")
+  set(PROCESSOR_IS_AARCH64 TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
   set(PROCESSOR_IS_ARM TRUE)
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
-  set(PROCESSOR_IS_AARCH64 TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64|amd64)|(^i.86$)")
   set(PROCESSOR_IS_X86 TRUE)
 elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
@@ -70,22 +65,19 @@
 macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
   list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpu_features_macros.h)
   list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpu_features_cache_info.h)
+  file(GLOB IMPL_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/impl_*.c")
+  list(APPEND ${SRCS_LIST_NAME} ${IMPL_SOURCES})
   if(PROCESSOR_IS_MIPS)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_mips.h)
-      list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/src/cpuinfo_mips.c)
   elseif(PROCESSOR_IS_ARM)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_arm.h)
-      list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/src/cpuinfo_arm.c)
   elseif(PROCESSOR_IS_AARCH64)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_aarch64.h)
-      list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/src/cpuinfo_aarch64.c)
   elseif(PROCESSOR_IS_X86)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_x86.h)
       list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/internal/cpuid_x86.h)
-      list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/src/cpuinfo_x86.c)
   elseif(PROCESSOR_IS_POWER)
       list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_ppc.h)
-      list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/src/cpuinfo_ppc.c)
   else()
     message(FATAL_ERROR "Unsupported architectures ${CMAKE_SYSTEM_PROCESSOR}")
   endif()
@@ -104,7 +96,6 @@
   ${PROJECT_SOURCE_DIR}/src/stack_line_reader.c
   ${PROJECT_SOURCE_DIR}/src/string_view.c
 )
-set_property(TARGET utils PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
 setup_include_and_definitions(utils)
 
 #
@@ -125,7 +116,6 @@
   if(HAVE_STRONG_GETAUXVAL)
     target_compile_definitions(unix_based_hardware_detection PRIVATE HAVE_STRONG_GETAUXVAL)
   endif()
-  set_property(TARGET unix_based_hardware_detection PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
 endif()
 
 #
@@ -142,7 +132,6 @@
 set_target_properties(cpu_features PROPERTIES PUBLIC_HEADER "${CPU_FEATURES_HDRS}")
 setup_include_and_definitions(cpu_features)
 target_link_libraries(cpu_features PUBLIC ${CMAKE_DL_LIBS})
-set_property(TARGET cpu_features PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC})
 target_include_directories(cpu_features
   PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpu_features>
 )
@@ -233,6 +222,7 @@
   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
 )
 install(EXPORT CpuFeaturesTargets
   NAMESPACE CpuFeatures::
diff --git a/METADATA b/METADATA
index 5e2b871..eac4f3d 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
     type: GIT
     value: "https://github.com/google/cpu_features.git"
   }
-  version: "v0.6.0"
+  version: "v0.7.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2020
-    month: 10
-    day: 15
+    year: 2022
+    month: 3
+    day: 8
   }
 }
diff --git a/README.md b/README.md
index 8a34168..6091845 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,14 @@
-# cpu_features [![Build Status](https://travis-ci.org/google/cpu_features.svg?branch=master)](https://travis-ci.org/google/cpu_features) [![Build status](https://ci.appveyor.com/api/projects/status/46d1owsj7n8dsylq/branch/master?svg=true)](https://ci.appveyor.com/project/gchatelet/cpu-features/branch/master)
+# cpu_features
+[![Linux Status][linux_svg]][linux_link]
+[![Macos Status][macos_svg]][macos_link]
+[![Windows Status][windows_svg]][windows_link]
+
+[linux_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_linux.yml/badge.svg?branch=main
+[linux_link]: https://github.com/google/cpu_features/actions/workflows/amd64_linux.yml
+[macos_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_macos.yml/badge.svg?branch=main
+[macos_link]: https://github.com/google/cpu_features/actions/workflows/amd64_macos.yml
+[windows_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_windows.yml/badge.svg?branch=main
+[windows_link]: https://github.com/google/cpu_features/actions/workflows/amd64_windows.yml
 
 A cross-platform C library to retrieve CPU features (such as available
 instructions) at runtime.
@@ -12,6 +22,7 @@
 - [Android NDK's drop in replacement](#ndk)
 - [License](#license)
 - [Build with cmake](#cmake)
+- [Community Bindings](#bindings)
 
 <a name="rationale"></a>
 ## Design Rationale
@@ -32,7 +43,7 @@
 <a name="codesample"></a>
 ## Code samples
 
-**Note:** For C++ code, the library functions are defined in the `CpuFeatures` namespace.
+**Note:** For C++ code, the library functions are defined in the `cpu_features` namespace.
 
 ### Checking features at runtime
 
@@ -42,7 +53,7 @@
 ```c
 #include "cpuinfo_x86.h"
 
-// For C++, add `using namespace CpuFeatures;`
+// For C++, add `using namespace cpu_features;`
 static const X86Features features = GetX86Info().features;
 
 void Compute(void) {
@@ -64,7 +75,7 @@
 #include <stdbool.h>
 #include "cpuinfo_arm.h"
 
-// For C++, add `using namespace CpuFeatures;`
+// For C++, add `using namespace cpu_features;`
 static const ArmFeatures features = GetArmInfo().features;
 static const bool has_aes_and_neon = features.aes && features.neon;
 
@@ -84,7 +95,7 @@
 #include <stdbool.h>
 #include "cpuinfo_x86.h"
 
-// For C++, add `using namespace CpuFeatures;`
+// For C++, add `using namespace cpu_features;`
 static const X86Features features = GetX86Info().features;
 static const bool has_avx = CPU_FEATURES_COMPILED_X86_AVX || features.avx;
 
@@ -107,7 +118,7 @@
 #include <stdbool.h>
 #include "cpuinfo_x86.h"
 
-// For C++, add `using namespace CpuFeatures;`
+// For C++, add `using namespace cpu_features;`
 static const X86Info info = GetX86Info();
 static const X86Microarchitecture uarch = GetX86Microarchitecture(&info);
 static const bool has_fast_avx = info.features.avx && uarch != INTEL_SNB;
@@ -141,13 +152,14 @@
 <a name="support"></a>
 ## What's supported
 
-|         | x86³ |   ARM   | AArch64 |  MIPS⁴ |  POWER  |
-|---------|:----:|:-------:|:-------:|:------:|:-------:|
-| Android | yes² |   yes¹  |   yes¹  |  yes¹  |   N/A   |
-| iOS     |  N/A | not yet | not yet |   N/A  |   N/A   |
-| Linux   | yes² |   yes¹  |   yes¹  |  yes¹  |   yes¹  |
-| MacOs   | yes² |   N/A   | not yet |   N/A  |    no   |
-| Windows | yes² | not yet | not yet |   N/A  |   N/A   |
+|         | x86³ | ARM     | AArch64 | MIPS⁴   | POWER   |
+|---------|:----:|:-------:|:-------:|:-------:|:-------:|
+| Android | yes² | yes¹    | yes¹    | yes¹    | N/A     |
+| iOS     | N/A  | not yet | not yet | N/A     | N/A     |
+| Linux   | yes² | yes¹    | yes¹    | yes¹    | yes¹    |
+| MacOs   | yes² | N/A     | not yet | N/A     | no      |
+| Windows | yes² | not yet | not yet | N/A     | N/A     |
+| FreeBSD | yes² | not yet | not yet | not yet | not yet |
 
 1.  **Features revealed from Linux.** We gather data from several sources
     depending on availability:
@@ -167,7 +179,7 @@
 ## Android NDK's drop in replacement
 
 [cpu_features](https://github.com/google/cpu_features) is now officially
-supporting Android and offers a drop in replacement of for the NDK's [cpu-features.h](https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures/cpu-features.h)
+supporting Android and offers a drop in replacement of for the NDK's [cpu-features.h](https://android.googlesource.com/platform/ndk/+/main/sources/android/cpufeatures/cpu-features.h)
 , see [ndk_compat](ndk_compat) folder for details.
 
 <a name="license"></a>
@@ -182,18 +194,35 @@
 Please check the [CMake build instructions](cmake/README.md).
 
 <a name="quickstart"></a>
-### Quickstart with `Ninja`
+### Quickstart
 
- - build `list_cpu_features`
+ - Run `list_cpu_features`
+```sh
+cmake -S. -Bbuild -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release
+cmake --build build --config Release -j
+./build/list_cpu_features --json
 ```
-    cmake -B/tmp/cpu_features -H. -GNinja -DCMAKE_BUILD_TYPE=Release
-    ninja -C/tmp/cpu_features
-    /tmp/cpu_features/list_cpu_features --json
-```
+
+_Note_: Use `--target ALL_BUILD` on the second line for `Visual Studio` and `XCode`.
 
  - run tests
+```sh
+cmake -S. -Bbuild -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug
+cmake --build build --config Debug -j
+cmake --build build --config Debug --target test
 ```
-    cmake -B/tmp/cpu_features -H. -GNinja -DBUILD_TESTING=ON
-    ninja -C/tmp/cpu_features
-    ninja -C/tmp/cpu_features test
-```
+
+_Note_: Use `--target RUN_TESTS` on the last line for `Visual Studio` and `--target RUN_TEST` for `XCode`.
+
+<a name="bindings"></a>
+## Community bindings
+
+Links provided here are not affiliated with Google but are kindly provided by the OSS Community.
+
+ - .Net
+   - https://github.com/toor1245/cpu_features.NET
+ - Python
+   - https://github.com/Narasimha1997/py_cpu
+
+
+_Send PR to showcase your wrapper here_
diff --git a/WORKSPACE b/WORKSPACE
index 8ea8a8b..104c459 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,7 +1,23 @@
-# ===== googletest =====
+workspace(name = "com_google_cpufeatures")
 
-git_repository(
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
     name = "com_google_googletest",
-    remote = "https://github.com/google/googletest.git",
-    commit = "c3f65335b79f47b05629e79a54685d899bc53b93",
+    sha256 = "269cebe2be1f607f91f52630ff5bec3275b948c65d4bac323ebd05e90d84f7a9",
+    strip_prefix = "googletest-e2239ee6043f73722e7aa812a459f54a28552929",
+    urls = ["https://github.com/google/googletest/archive/e2239ee6043f73722e7aa812a459f54a28552929.zip"],
 )
+
+http_archive(
+    name = "bazel_skylib",
+    sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d",
+    urls = [
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
+        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz",
+    ],
+)
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+
+bazel_skylib_workspace()
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index f18635a..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-version: '{build}'
-shallow_clone: true
-
-platform: x64
-
-environment:
-  matrix:
-  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
-    CMAKE_GENERATOR: "Visual Studio 15 2017 Win64"
-  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
-    CMAKE_GENERATOR: "Visual Studio 14 2015 Win64"
-
-matrix:
-  fast_finish: true
-
-before_build:
-  - cmake --version
-  - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -H. -Bcmake_build -G "%CMAKE_GENERATOR%"
-
-build_script:
-  - cmake --build cmake_build --config Debug --target ALL_BUILD
-
-test_script:
-  - cmake --build cmake_build --config Debug --target RUN_TESTS
diff --git a/bazel/platforms.bzl b/bazel/platforms.bzl
new file mode 100644
index 0000000..5671add
--- /dev/null
+++ b/bazel/platforms.bzl
@@ -0,0 +1,11 @@
+"""Defines global variables that lists target cpus"""
+
+PLATFORM_CPU_X86_64 = ("@platforms//cpu:x86_64")
+
+PLATFORM_CPU_ARM = ("@platforms//cpu:arm")
+
+PLATFORM_CPU_ARM64 = ("@platforms//cpu:arm64")
+
+PLATFORM_CPU_MIPS = ("@platforms//cpu:mips64")
+
+PLATFORM_CPU_PPC = ("@platforms//cpu:ppc")
diff --git a/ci/Makefile b/ci/Makefile
new file mode 100644
index 0000000..47ea30a
--- /dev/null
+++ b/ci/Makefile
@@ -0,0 +1,240 @@
+PROJECT := cpu_features
+BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
+SHA1 := $(shell git rev-parse --verify HEAD)
+
+# General commands
+.PHONY: help
+BOLD=\e[1m
+RESET=\e[0m
+
+help:
+	@echo -e "${BOLD}SYNOPSIS${RESET}"
+	@echo -e "\tmake <target> [NOCACHE=1]"
+	@echo
+	@echo -e "${BOLD}DESCRIPTION${RESET}"
+	@echo -e "\ttest build inside docker container to have a reproductible build."
+	@echo
+	@echo -e "${BOLD}MAKE TARGETS${RESET}"
+	@echo -e "\t${BOLD}help${RESET}: display this help and exit."
+	@echo
+	@echo -e "\t${BOLD}amd64_<stage>${RESET}: build <stage> docker image using an Ubuntu:latest x86_64 base image."
+	@echo -e "\t${BOLD}save_amd64_<stage>${RESET}: Save the <stage> docker image."
+	@echo -e "\t${BOLD}sh_amd64_<stage>${RESET}: run a container using the <stage> docker image (debug purpose)."
+	@echo -e "\t${BOLD}clean_amd64_<stage>${RESET}: Remove cache and docker image."
+	@echo
+	@echo -e "\tWith ${BOLD}<stage>${RESET}:"
+	@echo -e "\t\t${BOLD}env${RESET}"
+	@echo -e "\t\t${BOLD}devel${RESET}"
+	@echo -e "\t\t${BOLD}build${RESET}"
+	@echo -e "\t\t${BOLD}test${RESET}"
+	@echo -e "\t\t${BOLD}install_env${RESET}"
+	@echo -e "\t\t${BOLD}install_devel${RESET}"
+	@echo -e "\t\t${BOLD}install_build${RESET}"
+	@echo -e "\t\t${BOLD}install_test${RESET}"
+	@echo -e "\te.g. 'make amd64_build'"
+	@echo
+	@echo -e "\t${BOLD}<target>_<toolchain_stage>${RESET}: build <stage> docker image for a specific toolchain target."
+	@echo -e "\t${BOLD}save_<target>_<toolchain_stage>${RESET}: Save the <stage> docker image for a specific platform."
+	@echo -e "\t${BOLD}sh_<target>_<toolchain_stage>${RESET}: run a container using the <stage> docker image specified (debug purpose)."
+	@echo -e "\t${BOLD}clean_<target>_<toolchain_stage>${RESET}: Remove cache and docker image."
+	@echo
+	@echo -e "\tWith ${BOLD}<target>${RESET}:"
+	@echo -e "\t\t${BOLD}arm-linux-gnueabihf${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}armv8l-linux-gnueabihf${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}arm-linux-gnueabi${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}armeb-linux-gnueabihf${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}armeb-linux-gnueabi${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}aarch64-linux-gnu${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}aarch64_be-linux-gnu${RESET} (linaro toolchain)"
+	@echo -e "\t\t${BOLD}mips32${RESET} (codespace toolchain)"
+	@echo -e "\t\t${BOLD}mips64${RESET} (codespace toolchain)"
+	@echo -e "\t\t${BOLD}mips32el${RESET} (codespace toolchain)"
+	@echo -e "\t\t${BOLD}mips64el${RESET} (codespace toolchain)"
+	@echo
+	@echo -e "\tWith ${BOLD}<toolchain_stage>${RESET}:"
+	@echo -e "\t\t${BOLD}env${RESET}"
+	@echo -e "\t\t${BOLD}devel${RESET}"
+	@echo -e "\t\t${BOLD}build${RESET}"
+	@echo -e "\t\t${BOLD}test${RESET}"
+	@echo -e "\te.g. 'make aarch64_test'"
+	@echo
+	@echo -e "\t${BOLD}<VM>${RESET}: build the vagrant <VM> virtual machine."
+	@echo -e "\t${BOLD}clean_<VM>${RESET}: Remove virtual machine for the specified vm."
+	@echo
+	@echo -e "\t${BOLD}<VM>${RESET}:"
+	@echo -e "\t\t${BOLD}freebsd${RESET} (FreeBSD)"
+	@echo
+	@echo -e "\t${BOLD}clean${RESET}: Remove cache and ALL docker images."
+	@echo
+	@echo -e "\t${BOLD}NOCACHE=1${RESET}: use 'docker build --no-cache' when building container (default use cache)."
+	@echo
+	@echo -e "branch: $(BRANCH)"
+	@echo -e "sha1: $(SHA1)"
+
+# Need to add cmd_platform to PHONY otherwise target are ignored since they do not
+# contain recipe (using FORCE do not work here)
+.PHONY: all
+all: build
+
+# Delete all implicit rules to speed up makefile
+MAKEFLAGS += --no-builtin-rules
+.SUFFIXES:
+# Remove some rules from gmake that .SUFFIXES does not remove.
+SUFFIXES =
+# Keep all intermediate files
+# ToDo: try to remove it later
+.SECONDARY:
+
+# Docker image name prefix.
+IMAGE := ${PROJECT}
+
+ifdef NOCACHE
+DOCKER_BUILD_CMD := docker build --no-cache
+else
+DOCKER_BUILD_CMD := docker build
+endif
+
+DOCKER_RUN_CMD := docker run --rm --init --net=host
+
+# $* stem
+# $< first prerequist
+# $@ target name
+
+############
+## NATIVE ##
+############
+STAGES = env devel build test install_env install_devel install_build install_test
+
+targets_amd64 = $(addprefix amd64_, $(STAGES))
+.PHONY: $(targets_amd64)
+$(targets_amd64): amd64_%: docker/amd64/Dockerfile
+	#@docker image rm -f ${IMAGE}:amd64_$* 2>/dev/null
+	${DOCKER_BUILD_CMD} \
+ --tag ${IMAGE}:amd64_$* \
+ --target=$* \
+ -f $< \
+ ..
+
+#$(info Create targets: save_amd64 $(addprefix save_amd64_, $(STAGES)) (debug).)
+save_targets_amd64 = $(addprefix save_amd64_, $(STAGES))
+.PHONY: $(save_targets_amd64)
+$(save_targets_amd64): save_amd64_%: cache/amd64/docker_%.tar
+cache/amd64/docker_%.tar: amd64_%
+	@rm -f $@
+	mkdir -p cache/amd64
+	docker save ${IMAGE}:amd64_$* -o $@
+
+#$(info Create targets: $(addprefix sh_amd64_, $(STAGES)) (debug).)
+sh_targets_amd64 = $(addprefix sh_amd64_, $(STAGES))
+.PHONY: $(sh_targets_amd64)
+$(sh_targets_amd64): sh_amd64_%: amd64_%
+	${DOCKER_RUN_CMD} -it --name ${IMAGE}_amd64_$* ${IMAGE}:amd64_$*
+
+#$(info Create targets: $(addprefix clean_amd64_, $(STAGES)).)
+clean_targets_amd64 = $(addprefix clean_amd64_, $(STAGES))
+.PHONY: clean_amd64 $(clean_targets_amd64)
+clean_amd64: $(clean_targets_amd64)
+$(clean_targets_amd64): clean_amd64_%:
+	docker image rm -f ${IMAGE}:amd64_$* 2>/dev/null
+	rm -f cache/amd64/docker_$*.tar
+
+
+###############
+## TOOLCHAIN ##
+###############
+TOOLCHAIN_TARGETS = \
+ arm-linux-gnueabihf armv8l-linux-gnueabihf arm-linux-gnueabi armeb-linux-gnueabihf armeb-linux-gnueabi \
+ aarch64-linux-gnu aarch64_be-linux-gnu \
+ mips32 mips32el mips64 mips64el
+TOOLCHAIN_STAGES = env devel build test
+define toolchain-stage-target =
+#$$(info STAGE: $1)
+#$$(info Create targets: toolchain_$1 $(addsuffix _$1, $(TOOLCHAIN_TARGETS)).)
+targets_toolchain_$1 = $(addsuffix _$1, $(TOOLCHAIN_TARGETS))
+.PHONY: toolchain_$1 $$(targets_toolchain_$1)
+toolchain_$1: $$(targets_toolchain_$1)
+$$(targets_toolchain_$1): %_$1: docker/toolchain/Dockerfile
+	#@docker image rm -f ${IMAGE}:$$*_$1 2>/dev/null
+	${DOCKER_BUILD_CMD} \
+ --tag ${IMAGE}:$$*_$1 \
+ --build-arg TARGET=$$* \
+ --target=$1 \
+ -f $$< \
+ ..
+
+#$$(info Create targets: save_toolchain_$1 $(addprefix save_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).)
+save_targets_toolchain_$1 = $(addprefix save_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
+.PHONY: save_toolchain_$1 $$(save_targets_toolchain_$1)
+save_toolchain_$1: $$(save_targets_toolchain_$1)
+$$(save_targets_toolchain_$1): save_%_$1: cache/%/docker_$1.tar
+cache/%/docker_$1.tar: %_$1
+	@rm -f $$@
+	mkdir -p cache/$$*
+	docker save ${IMAGE}:$$*_$1 -o $$@
+
+#$$(info Create targets: $(addprefix sh_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).)
+sh_targets_toolchain_$1 = $(addprefix sh_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
+.PHONY: $$(sh_targets_toolchain_$1)
+$$(sh_targets_toolchain_$1): sh_%_$1: %_$1
+	${DOCKER_RUN_CMD} -it --name ${IMAGE}_$$*_$1 ${IMAGE}:$$*_$1
+
+#$$(info Create targets: clean_toolchain_$1 $(addprefix clean_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))).)
+clean_targets_toolchain_$1 = $(addprefix clean_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
+.PHONY: clean_toolchain_$1 $$(clean_targets_toolchain_$1)
+clean_toolchain_$1: $$(clean_targets_toolchain_$1)
+$$(clean_targets_toolchain_$1): clean_%_$1:
+	docker image rm -f ${IMAGE}:$$*_$1 2>/dev/null
+	rm -f cache/$$*/docker_$1.tar
+endef
+
+$(foreach stage,$(TOOLCHAIN_STAGES),$(eval $(call toolchain-stage-target,$(stage))))
+
+## MERGE ##
+.PHONY: clean_toolchain
+clean_toolchain: $(addprefix clean_toolchain_, $(TOOLCHAIN_STAGES))
+	-rmdir $(addprefix cache/, $(TOOLCHAIN_TARGETS))
+
+.PHONY: env devel build test
+env: amd64_env toolchain_env
+devel: amd64_devel toolchain_devel
+build: amd64_build toolchain_build
+test: amd64_test toolchain_test
+
+.PHONY: install_env install_devel install_build install_test
+install_env: amd64_install_env
+install_devel: amd64_install_devel
+install_build: amd64_install_build
+install_test: amd64_install_test
+
+#############
+## VAGRANT ##
+#############
+VMS = freebsd
+
+vms_targets = $(addsuffix _build, $(VMS))
+.PHONY: $(vms_targets)
+$(vms_targets): %_build: vagrant/%/Vagrantfile
+	@cd vagrant/$* && vagrant destroy -f
+	cd vagrant/$* && vagrant up
+
+clean_vms_targets = $(addprefix clean_, $(VMS))
+.PHONY: clean_vms $(clean_vms_targets)
+clean_vms: $(clean_vms_targets)
+$(clean_vms_targets): clean_%:
+	cd vagrant/$* && vagrant destroy -f
+	-rm -rf vagrant/$*/.vagrant
+
+###########
+## CLEAN ##
+###########
+.PHONY: clean
+clean: clean_amd64 clean_toolchain clean_vms
+	docker container prune -f
+	docker image prune -f
+	-rmdir cache
+
+.PHONY: distclean
+distclean: clean
+	-docker container rm -f $$(docker container ls -aq)
+	-docker image rm -f $$(docker image ls -aq)
+	-vagrant box remove -f generic/freebsd12
diff --git a/ci/README.md b/ci/README.md
new file mode 100644
index 0000000..e370136
--- /dev/null
+++ b/ci/README.md
@@ -0,0 +1,66 @@
+# GitHub-CI Status
+| OS       | amd64 | AArch64 | ARM | MIPS |
+|:-------- | :----: | :-----: | :-: | :--: |
+| FreeBSD    | [![Status][freebsd_svg]][freebsd_link] | N/A | N/A | N/A |
+| Linux    | [![Status][linux_svg]][linux_link] | [![Status][linux_aarch64_svg]][linux_aarch64_link] | [![Status][linux_arm_svg]][linux_arm_link] | [![Status][linux_mips_svg]][linux_mips_link] |
+| MacOS    | [![Status][macos_svg]][macos_link] | N/A | N/A | N/A |
+| Windows  | [![Status][windows_svg]][windows_link] | N/A | N/A | N/A |
+
+[freebsd_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_freebsd.yml/badge.svg?branch=main
+[freebsd_link]: https://github.com/google/cpu_features/actions/workflows/amd64_freebsd.yml
+
+[linux_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_linux.yml/badge.svg?branch=main
+[linux_link]: https://github.com/google/cpu_features/actions/workflows/amd64_linux.yml
+[linux_aarch64_svg]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux.yml/badge.svg?branch=main
+[linux_aarch64_link]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux.yml
+[linux_arm_svg]: https://github.com/google/cpu_features/actions/workflows/arm_linux.yml/badge.svg?branch=main
+[linux_arm_link]: https://github.com/google/cpu_features/actions/workflows/arm_linux.yml
+[linux_mips_svg]: https://github.com/google/cpu_features/actions/workflows/mips_linux.yml/badge.svg?branch=main
+[linux_mips_link]: https://github.com/google/cpu_features/actions/workflows/mips_linux.yml
+
+[macos_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_macos.yml/badge.svg?branch=main
+[macos_link]: https://github.com/google/cpu_features/actions/workflows/amd64_macos.yml
+
+[windows_svg]: https://github.com/google/cpu_features/actions/workflows/amd64_windows.yml/badge.svg?branch=main
+[windows_link]: https://github.com/google/cpu_features/actions/workflows/amd64_windows.yml
+
+## Makefile/Docker testing
+To test the build on various distro, we are using docker containers and a Makefile for orchestration.
+
+pros:
+* You are independent of third party CI runner config
+  (e.g. [github action virtual-environnments](https://github.com/actions/virtual-environments)).
+* You can run it locally on your linux system.
+* Most CI provide runners with docker and Makefile installed.
+
+cons:
+* Only GNU/Linux distro supported.
+
+### Usage
+To get the help simply type:
+```sh
+make
+```
+
+note: you can also use from top directory
+```sh
+make --directory=ci
+```
+
+### Example
+For example to test mips32 inside an container:
+```sh
+make mips32_test
+```
+
+### Docker layers
+Dockerfile is splitted in several stages.
+
+![docker](doc/docker.svg)
+
+
+## Makefile/Vagrant testing
+To test build for FreeBSD we are using Vagrant and VirtualBox box.
+
+This is similar to the docker stuff but use `vagrant` as `docker` cli and
+VirtuaBox to replace the docker engine daemon.
diff --git a/ci/doc/docker.dot b/ci/doc/docker.dot
new file mode 100644
index 0000000..a00ef1f
--- /dev/null
+++ b/ci/doc/docker.dot
@@ -0,0 +1,64 @@
+@startdot
+digraph DockerDeps {
+  //rankdir=BT;
+  rankdir=TD;
+  node [shape=cylinder, style="rounded,filled", color=black, fillcolor=royalblue];
+  DISTRO_IMG [label="ubuntu:latest"];
+  PKG [label="packages\ne.g. cmake, g++", shape=box3d];
+  SRC [label="git repo", shape=folder];
+  SPL [label="sample", shape=folder];
+
+  subgraph clusterDockerfile {
+    ENV_IMG [label="cpu_features:amd64_env\nenv"];
+    DEVEL_IMG [label="cpu_features:amd64_devel\ndevel"];
+    BUILD_IMG [label="cpu_features:amd64_build\nbuild"];
+    TEST_IMG [label="cpu_features:amd64_test\ntest"];
+    INSTALL_ENV_IMG [label="cpu_features:amd64_install_env\ninstall_env"];
+    INSTALL_DEVEL_IMG [label="cpu_features:amd64_install_devel\ninstall_devel"];
+    INSTALL_BUILD_IMG [label="cpu_features:amd64_install_build\ninstall_build"];
+    INSTALL_TEST_IMG [label="cpu_features:amd64_install_test\ninstall_test"];
+
+    ENV_IMG -> DEVEL_IMG;
+    DEVEL_IMG -> BUILD_IMG;
+    BUILD_IMG -> TEST_IMG;
+
+    ENV_IMG -> INSTALL_ENV_IMG;
+    BUILD_IMG -> INSTALL_ENV_IMG [label="copy install", style="dashed"];
+    INSTALL_ENV_IMG -> INSTALL_DEVEL_IMG;
+    SPL -> INSTALL_DEVEL_IMG [label="copy", style="dashed"];
+    INSTALL_DEVEL_IMG -> INSTALL_BUILD_IMG;
+    INSTALL_BUILD_IMG -> INSTALL_TEST_IMG;
+
+    color=royalblue;
+    label = "docker/amd64/Dockerfile";
+  }
+  DISTRO_IMG -> ENV_IMG;
+  PKG -> ENV_IMG [label="install", style="dashed"];
+  SRC -> DEVEL_IMG [label="copy", style="dashed"];
+
+  subgraph clusterCache {
+    node [shape=note, style="rounded,filled", color=black, fillcolor=royalblue];
+    ENV_TAR [label="docker_amd64_env.tar"];
+    DEVEL_TAR [label="docker_amd64_devel.tar"];
+    BUILD_TAR [label="docker_amd64_build.tar"];
+    TEST_TAR [label="docker_amd64_test.tar"];
+    INSTALL_ENV_TAR [label="docker_amd64_install_env.tar"];
+    INSTALL_DEVEL_TAR [label="docker_amd64_install_devel.tar"];
+    INSTALL_BUILD_TAR [label="docker_amd64_install_build.tar"];
+    INSTALL_TEST_TAR [label="docker_amd64_install_test.tar"];
+
+    edge [color=red];
+    ENV_IMG -> ENV_TAR [label="make save_amd64_env"];
+    DEVEL_IMG -> DEVEL_TAR [label="make save_amd64_devel"];
+    BUILD_IMG -> BUILD_TAR [label="make save_amd64_build"];
+    TEST_IMG -> TEST_TAR [label="make save_amd64_test"];
+    INSTALL_ENV_IMG -> INSTALL_ENV_TAR [label="make save_amd64_install_env"];
+    INSTALL_DEVEL_IMG -> INSTALL_DEVEL_TAR [label="make save_amd64_install_devel"];
+    INSTALL_BUILD_IMG -> INSTALL_BUILD_TAR [label="make save_amd64_install_build"];
+    INSTALL_TEST_IMG -> INSTALL_TEST_TAR [label="make save_amd64_install_test"];
+
+    color=royalblue;
+    label = "cache/amd64/";
+  }
+}
+@enddot
diff --git a/ci/doc/docker.svg b/ci/doc/docker.svg
new file mode 100644
index 0000000..bd9bd6d
--- /dev/null
+++ b/ci/doc/docker.svg
@@ -0,0 +1,312 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.49.2 (0)
+ -->
+<!-- Title: DockerDeps Pages: 1 -->
+<svg width="1904pt" height="900pt"
+ viewBox="0.00 0.00 1904.00 899.75" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 895.75)">
+<title>DockerDeps</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-895.75 1900,-895.75 1900,4 -4,4"/>
+<g id="clust1" class="cluster">
+<title>clusterDockerfile</title>
+<polygon fill="none" stroke="royalblue" points="691,-116 691,-812.75 1253,-812.75 1253,-116 691,-116"/>
+<text text-anchor="middle" x="972" y="-797.55" font-family="Times,serif" font-size="14.00">docker/amd64/Dockerfile</text>
+</g>
+<g id="clust2" class="cluster">
+<title>clusterCache</title>
+<polygon fill="none" stroke="royalblue" points="8,-8 8,-83 1826,-83 1826,-8 8,-8"/>
+<text text-anchor="middle" x="917" y="-67.8" font-family="Times,serif" font-size="14.00">cache/amd64/</text>
+</g>
+<!-- DISTRO_IMG -->
+<g id="node1" class="node">
+<title>DISTRO_IMG</title>
+<path fill="royalblue" stroke="black" d="M893.5,-887.48C893.5,-889.28 868.18,-890.75 837,-890.75 805.82,-890.75 780.5,-889.28 780.5,-887.48 780.5,-887.48 780.5,-858.02 780.5,-858.02 780.5,-856.22 805.82,-854.75 837,-854.75 868.18,-854.75 893.5,-856.22 893.5,-858.02 893.5,-858.02 893.5,-887.48 893.5,-887.48"/>
+<path fill="none" stroke="black" d="M893.5,-887.48C893.5,-885.67 868.18,-884.2 837,-884.2 805.82,-884.2 780.5,-885.67 780.5,-887.48"/>
+<text text-anchor="middle" x="837" y="-869.05" font-family="Times,serif" font-size="14.00">ubuntu:latest</text>
+</g>
+<!-- ENV_IMG -->
+<g id="node5" class="node">
+<title>ENV_IMG</title>
+<path fill="royalblue" stroke="black" d="M1005,-777.1C1005,-779.74 961.52,-781.88 908,-781.88 854.48,-781.88 811,-779.74 811,-777.1 811,-777.1 811,-734.15 811,-734.15 811,-731.51 854.48,-729.37 908,-729.37 961.52,-729.37 1005,-731.51 1005,-734.15 1005,-734.15 1005,-777.1 1005,-777.1"/>
+<path fill="none" stroke="black" d="M1005,-777.1C1005,-774.47 961.52,-772.33 908,-772.33 854.48,-772.33 811,-774.47 811,-777.1"/>
+<text text-anchor="middle" x="908" y="-759.42" font-family="Times,serif" font-size="14.00">cpu_features:amd64_env</text>
+<text text-anchor="middle" x="908" y="-744.42" font-family="Times,serif" font-size="14.00">env</text>
+</g>
+<!-- DISTRO_IMG&#45;&gt;ENV_IMG -->
+<g id="edge10" class="edge">
+<title>DISTRO_IMG&#45;&gt;ENV_IMG</title>
+<path fill="none" stroke="black" d="M847.78,-854.26C858.17,-837.42 874.16,-811.49 887.05,-790.59"/>
+<polygon fill="black" stroke="black" points="890.09,-792.33 892.37,-781.98 884.14,-788.65 890.09,-792.33"/>
+</g>
+<!-- PKG -->
+<g id="node2" class="node">
+<title>PKG</title>
+<polygon fill="royalblue" stroke="black" points="1046.5,-891.75 915.5,-891.75 911.5,-887.75 911.5,-853.75 1042.5,-853.75 1046.5,-857.75 1046.5,-891.75"/>
+<polyline fill="none" stroke="black" points="1042.5,-887.75 911.5,-887.75 "/>
+<polyline fill="none" stroke="black" points="1042.5,-887.75 1042.5,-853.75 "/>
+<polyline fill="none" stroke="black" points="1042.5,-887.75 1046.5,-891.75 "/>
+<text text-anchor="middle" x="979" y="-876.55" font-family="Times,serif" font-size="14.00">packages</text>
+<text text-anchor="middle" x="979" y="-861.55" font-family="Times,serif" font-size="14.00">e.g. cmake, g++</text>
+</g>
+<!-- PKG&#45;&gt;ENV_IMG -->
+<g id="edge11" class="edge">
+<title>PKG&#45;&gt;ENV_IMG</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M967.75,-853.51C957.4,-836.72 941.77,-811.38 929.09,-790.83"/>
+<polygon fill="black" stroke="black" points="931.91,-788.73 923.69,-782.06 925.96,-792.41 931.91,-788.73"/>
+<text text-anchor="middle" x="978.5" y="-824.55" font-family="Times,serif" font-size="14.00">install</text>
+</g>
+<!-- SRC -->
+<g id="node3" class="node">
+<title>SRC</title>
+<polygon fill="royalblue" stroke="black" points="1334.5,-773.62 1331.5,-777.62 1310.5,-777.62 1307.5,-773.62 1261.5,-773.62 1261.5,-737.62 1334.5,-737.62 1334.5,-773.62"/>
+<text text-anchor="middle" x="1298" y="-751.92" font-family="Times,serif" font-size="14.00">git repo</text>
+</g>
+<!-- DEVEL_IMG -->
+<g id="node6" class="node">
+<title>DEVEL_IMG</title>
+<path fill="royalblue" stroke="black" d="M1189,-673.85C1189,-676.49 1142.83,-678.63 1086,-678.63 1029.17,-678.63 983,-676.49 983,-673.85 983,-673.85 983,-630.9 983,-630.9 983,-628.26 1029.17,-626.12 1086,-626.12 1142.83,-626.12 1189,-628.26 1189,-630.9 1189,-630.9 1189,-673.85 1189,-673.85"/>
+<path fill="none" stroke="black" d="M1189,-673.85C1189,-671.22 1142.83,-669.08 1086,-669.08 1029.17,-669.08 983,-671.22 983,-673.85"/>
+<text text-anchor="middle" x="1086" y="-656.17" font-family="Times,serif" font-size="14.00">cpu_features:amd64_devel</text>
+<text text-anchor="middle" x="1086" y="-641.17" font-family="Times,serif" font-size="14.00">devel</text>
+</g>
+<!-- SRC&#45;&gt;DEVEL_IMG -->
+<g id="edge12" class="edge">
+<title>SRC&#45;&gt;DEVEL_IMG</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1271.39,-737.62C1266.66,-734.8 1261.73,-731.99 1257,-729.5 1224.81,-712.57 1188.08,-695.89 1156.94,-682.48"/>
+<polygon fill="black" stroke="black" points="1158.03,-679.14 1147.46,-678.43 1155.28,-685.58 1158.03,-679.14"/>
+<text text-anchor="middle" x="1237" y="-700.3" font-family="Times,serif" font-size="14.00">copy</text>
+</g>
+<!-- SPL -->
+<g id="node4" class="node">
+<title>SPL</title>
+<polygon fill="royalblue" stroke="black" points="767,-477.88 764,-481.88 743,-481.88 740,-477.88 699,-477.88 699,-441.88 767,-441.88 767,-477.88"/>
+<text text-anchor="middle" x="733" y="-456.18" font-family="Times,serif" font-size="14.00">sample</text>
+</g>
+<!-- INSTALL_DEVEL_IMG -->
+<g id="node10" class="node">
+<title>INSTALL_DEVEL_IMG</title>
+<path fill="royalblue" stroke="black" d="M956.5,-378.1C956.5,-380.74 898.9,-382.88 828,-382.88 757.1,-382.88 699.5,-380.74 699.5,-378.1 699.5,-378.1 699.5,-335.15 699.5,-335.15 699.5,-332.51 757.1,-330.37 828,-330.37 898.9,-330.37 956.5,-332.51 956.5,-335.15 956.5,-335.15 956.5,-378.1 956.5,-378.1"/>
+<path fill="none" stroke="black" d="M956.5,-378.1C956.5,-375.47 898.9,-373.33 828,-373.33 757.1,-373.33 699.5,-375.47 699.5,-378.1"/>
+<text text-anchor="middle" x="828" y="-360.43" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_devel</text>
+<text text-anchor="middle" x="828" y="-345.43" font-family="Times,serif" font-size="14.00">install_devel</text>
+</g>
+<!-- SPL&#45;&gt;INSTALL_DEVEL_IMG -->
+<g id="edge7" class="edge">
+<title>SPL&#45;&gt;INSTALL_DEVEL_IMG</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M749.12,-441.7C762.24,-427.71 781.16,-407.54 797.18,-390.47"/>
+<polygon fill="black" stroke="black" points="800.02,-392.56 804.31,-382.87 794.91,-387.77 800.02,-392.56"/>
+<text text-anchor="middle" x="803" y="-404.55" font-family="Times,serif" font-size="14.00">copy</text>
+</g>
+<!-- ENV_IMG&#45;&gt;DEVEL_IMG -->
+<g id="edge1" class="edge">
+<title>ENV_IMG&#45;&gt;DEVEL_IMG</title>
+<path fill="none" stroke="black" d="M952.46,-729.34C976.87,-715.45 1007.3,-698.14 1032.95,-683.55"/>
+<polygon fill="black" stroke="black" points="1034.84,-686.5 1041.8,-678.52 1031.38,-680.42 1034.84,-686.5"/>
+</g>
+<!-- INSTALL_ENV_IMG -->
+<g id="node9" class="node">
+<title>INSTALL_ENV_IMG</title>
+<path fill="royalblue" stroke="black" d="M1030.5,-481.35C1030.5,-483.99 975.59,-486.13 908,-486.13 840.41,-486.13 785.5,-483.99 785.5,-481.35 785.5,-481.35 785.5,-438.4 785.5,-438.4 785.5,-435.76 840.41,-433.62 908,-433.62 975.59,-433.62 1030.5,-435.76 1030.5,-438.4 1030.5,-438.4 1030.5,-481.35 1030.5,-481.35"/>
+<path fill="none" stroke="black" d="M1030.5,-481.35C1030.5,-478.72 975.59,-476.58 908,-476.58 840.41,-476.58 785.5,-478.72 785.5,-481.35"/>
+<text text-anchor="middle" x="908" y="-463.68" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_env</text>
+<text text-anchor="middle" x="908" y="-448.68" font-family="Times,serif" font-size="14.00">install_env</text>
+</g>
+<!-- ENV_IMG&#45;&gt;INSTALL_ENV_IMG -->
+<g id="edge4" class="edge">
+<title>ENV_IMG&#45;&gt;INSTALL_ENV_IMG</title>
+<path fill="none" stroke="black" d="M908,-729.33C908,-676.94 908,-556.49 908,-496.37"/>
+<polygon fill="black" stroke="black" points="911.5,-496.22 908,-486.22 904.5,-496.22 911.5,-496.22"/>
+</g>
+<!-- ENV_TAR -->
+<g id="node13" class="node">
+<title>ENV_TAR</title>
+<polygon fill="royalblue" stroke="black" points="186,-52 16,-52 16,-16 192,-16 192,-46 186,-52"/>
+<polyline fill="none" stroke="black" points="186,-52 186,-46 "/>
+<polyline fill="none" stroke="black" points="192,-46 186,-46 "/>
+<text text-anchor="middle" x="104" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_env.tar</text>
+</g>
+<!-- ENV_IMG&#45;&gt;ENV_TAR -->
+<g id="edge13" class="edge">
+<title>ENV_IMG&#45;&gt;ENV_TAR</title>
+<path fill="none" stroke="red" d="M810.87,-751.31C609.47,-743.14 165,-717.82 165,-653.38 165,-653.38 165,-653.38 165,-149.12 165,-115.26 143.85,-81.71 126.46,-59.84"/>
+<polygon fill="red" stroke="red" points="129.09,-57.54 120.03,-52.05 123.7,-61.99 129.09,-57.54"/>
+<text text-anchor="middle" x="246.5" y="-404.55" font-family="Times,serif" font-size="14.00">make save_amd64_env</text>
+</g>
+<!-- BUILD_IMG -->
+<g id="node7" class="node">
+<title>BUILD_IMG</title>
+<path fill="royalblue" stroke="black" d="M1245,-584.6C1245,-587.24 1199.28,-589.38 1143,-589.38 1086.72,-589.38 1041,-587.24 1041,-584.6 1041,-584.6 1041,-541.65 1041,-541.65 1041,-539.01 1086.72,-536.87 1143,-536.87 1199.28,-536.87 1245,-539.01 1245,-541.65 1245,-541.65 1245,-584.6 1245,-584.6"/>
+<path fill="none" stroke="black" d="M1245,-584.6C1245,-581.97 1199.28,-579.83 1143,-579.83 1086.72,-579.83 1041,-581.97 1041,-584.6"/>
+<text text-anchor="middle" x="1143" y="-566.92" font-family="Times,serif" font-size="14.00">cpu_features:amd64_build</text>
+<text text-anchor="middle" x="1143" y="-551.92" font-family="Times,serif" font-size="14.00">build</text>
+</g>
+<!-- DEVEL_IMG&#45;&gt;BUILD_IMG -->
+<g id="edge2" class="edge">
+<title>DEVEL_IMG&#45;&gt;BUILD_IMG</title>
+<path fill="none" stroke="black" d="M1102.49,-626.14C1108.28,-617.28 1114.88,-607.17 1121.04,-597.73"/>
+<polygon fill="black" stroke="black" points="1124.01,-599.59 1126.55,-589.3 1118.15,-595.76 1124.01,-599.59"/>
+</g>
+<!-- DEVEL_TAR -->
+<g id="node14" class="node">
+<title>DEVEL_TAR</title>
+<polygon fill="royalblue" stroke="black" points="395.5,-52 210.5,-52 210.5,-16 401.5,-16 401.5,-46 395.5,-52"/>
+<polyline fill="none" stroke="black" points="395.5,-52 395.5,-46 "/>
+<polyline fill="none" stroke="black" points="401.5,-46 395.5,-46 "/>
+<text text-anchor="middle" x="306" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_devel.tar</text>
+</g>
+<!-- DEVEL_IMG&#45;&gt;DEVEL_TAR -->
+<g id="edge14" class="edge">
+<title>DEVEL_IMG&#45;&gt;DEVEL_TAR</title>
+<path fill="none" stroke="red" d="M982.94,-648.59C792.56,-642.05 405,-621.67 405,-564.12 405,-564.12 405,-564.12 405,-149.12 405,-110.26 371.83,-78.15 343.9,-58"/>
+<polygon fill="red" stroke="red" points="345.65,-54.96 335.44,-52.14 341.66,-60.71 345.65,-54.96"/>
+<text text-anchor="middle" x="493" y="-352.93" font-family="Times,serif" font-size="14.00">make save_amd64_devel</text>
+</g>
+<!-- TEST_IMG -->
+<g id="node8" class="node">
+<title>TEST_IMG</title>
+<path fill="royalblue" stroke="black" d="M1245,-481.35C1245,-483.99 1201.07,-486.13 1147,-486.13 1092.93,-486.13 1049,-483.99 1049,-481.35 1049,-481.35 1049,-438.4 1049,-438.4 1049,-435.76 1092.93,-433.62 1147,-433.62 1201.07,-433.62 1245,-435.76 1245,-438.4 1245,-438.4 1245,-481.35 1245,-481.35"/>
+<path fill="none" stroke="black" d="M1245,-481.35C1245,-478.72 1201.07,-476.58 1147,-476.58 1092.93,-476.58 1049,-478.72 1049,-481.35"/>
+<text text-anchor="middle" x="1147" y="-463.68" font-family="Times,serif" font-size="14.00">cpu_features:amd64_test</text>
+<text text-anchor="middle" x="1147" y="-448.68" font-family="Times,serif" font-size="14.00">test</text>
+</g>
+<!-- BUILD_IMG&#45;&gt;TEST_IMG -->
+<g id="edge3" class="edge">
+<title>BUILD_IMG&#45;&gt;TEST_IMG</title>
+<path fill="none" stroke="black" d="M1144,-536.84C1144.48,-524.63 1145.07,-509.79 1145.59,-496.47"/>
+<polygon fill="black" stroke="black" points="1149.1,-496.32 1146,-486.19 1142.11,-496.05 1149.1,-496.32"/>
+</g>
+<!-- BUILD_IMG&#45;&gt;INSTALL_ENV_IMG -->
+<g id="edge5" class="edge">
+<title>BUILD_IMG&#45;&gt;INSTALL_ENV_IMG</title>
+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1084.61,-536.97C1051.68,-522.78 1010.38,-504.99 976,-490.17"/>
+<polygon fill="black" stroke="black" points="977.07,-486.82 966.5,-486.08 974.3,-493.25 977.07,-486.82"/>
+<text text-anchor="middle" x="1080" y="-507.8" font-family="Times,serif" font-size="14.00">copy install</text>
+</g>
+<!-- BUILD_TAR -->
+<g id="node15" class="node">
+<title>BUILD_TAR</title>
+<polygon fill="royalblue" stroke="black" points="1812,-52 1630,-52 1630,-16 1818,-16 1818,-46 1812,-52"/>
+<polyline fill="none" stroke="black" points="1812,-52 1812,-46 "/>
+<polyline fill="none" stroke="black" points="1818,-46 1812,-46 "/>
+<text text-anchor="middle" x="1724" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_build.tar</text>
+</g>
+<!-- BUILD_IMG&#45;&gt;BUILD_TAR -->
+<g id="edge15" class="edge">
+<title>BUILD_IMG&#45;&gt;BUILD_TAR</title>
+<path fill="none" stroke="red" d="M1245.18,-554.53C1411.4,-540.79 1722,-508.71 1722,-460.88 1722,-460.88 1722,-460.88 1722,-149.12 1722,-119.36 1722.69,-85.23 1723.26,-62.11"/>
+<polygon fill="red" stroke="red" points="1726.76,-62.1 1723.52,-52.01 1719.76,-61.92 1726.76,-62.1"/>
+<text text-anchor="middle" x="1809" y="-301.3" font-family="Times,serif" font-size="14.00">make save_amd64_build</text>
+</g>
+<!-- TEST_TAR -->
+<g id="node16" class="node">
+<title>TEST_TAR</title>
+<polygon fill="royalblue" stroke="black" points="1606,-52 1432,-52 1432,-16 1612,-16 1612,-46 1606,-52"/>
+<polyline fill="none" stroke="black" points="1606,-52 1606,-46 "/>
+<polyline fill="none" stroke="black" points="1612,-46 1606,-46 "/>
+<text text-anchor="middle" x="1522" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_test.tar</text>
+</g>
+<!-- TEST_IMG&#45;&gt;TEST_TAR -->
+<g id="edge16" class="edge">
+<title>TEST_IMG&#45;&gt;TEST_TAR</title>
+<path fill="none" stroke="red" d="M1245.07,-451.87C1356.77,-441.13 1524,-415.4 1524,-357.62 1524,-357.62 1524,-357.62 1524,-149.12 1524,-119.36 1523.31,-85.23 1522.74,-62.11"/>
+<polygon fill="red" stroke="red" points="1526.24,-61.92 1522.48,-52.01 1519.24,-62.1 1526.24,-61.92"/>
+<text text-anchor="middle" x="1607" y="-249.68" font-family="Times,serif" font-size="14.00">make save_amd64_test</text>
+</g>
+<!-- INSTALL_ENV_IMG&#45;&gt;INSTALL_DEVEL_IMG -->
+<g id="edge6" class="edge">
+<title>INSTALL_ENV_IMG&#45;&gt;INSTALL_DEVEL_IMG</title>
+<path fill="none" stroke="black" d="M888.02,-433.59C877.81,-420.66 865.25,-404.76 854.26,-390.86"/>
+<polygon fill="black" stroke="black" points="856.95,-388.62 848,-382.94 851.46,-392.96 856.95,-388.62"/>
+</g>
+<!-- INSTALL_ENV_TAR -->
+<g id="node17" class="node">
+<title>INSTALL_ENV_TAR</title>
+<polygon fill="royalblue" stroke="black" points="1407.5,-52 1186.5,-52 1186.5,-16 1413.5,-16 1413.5,-46 1407.5,-52"/>
+<polyline fill="none" stroke="black" points="1407.5,-52 1407.5,-46 "/>
+<polyline fill="none" stroke="black" points="1413.5,-46 1407.5,-46 "/>
+<text text-anchor="middle" x="1300" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_env.tar</text>
+</g>
+<!-- INSTALL_ENV_IMG&#45;&gt;INSTALL_ENV_TAR -->
+<g id="edge17" class="edge">
+<title>INSTALL_ENV_IMG&#45;&gt;INSTALL_ENV_TAR</title>
+<path fill="none" stroke="red" d="M1012.88,-434.97C1121.03,-409.58 1274,-371.26 1274,-357.62 1274,-357.62 1274,-357.62 1274,-149.12 1274,-118.64 1282.93,-84.69 1290.32,-61.81"/>
+<polygon fill="red" stroke="red" points="1293.71,-62.72 1293.57,-52.12 1287.07,-60.49 1293.71,-62.72"/>
+<text text-anchor="middle" x="1381" y="-249.68" font-family="Times,serif" font-size="14.00">make save_amd64_install_env</text>
+</g>
+<!-- INSTALL_BUILD_IMG -->
+<g id="node11" class="node">
+<title>INSTALL_BUILD_IMG</title>
+<path fill="royalblue" stroke="black" d="M955.5,-274.85C955.5,-277.49 898.35,-279.63 828,-279.63 757.65,-279.63 700.5,-277.49 700.5,-274.85 700.5,-274.85 700.5,-231.9 700.5,-231.9 700.5,-229.26 757.65,-227.12 828,-227.12 898.35,-227.12 955.5,-229.26 955.5,-231.9 955.5,-231.9 955.5,-274.85 955.5,-274.85"/>
+<path fill="none" stroke="black" d="M955.5,-274.85C955.5,-272.22 898.35,-270.08 828,-270.08 757.65,-270.08 700.5,-272.22 700.5,-274.85"/>
+<text text-anchor="middle" x="828" y="-257.18" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_build</text>
+<text text-anchor="middle" x="828" y="-242.18" font-family="Times,serif" font-size="14.00">install_build</text>
+</g>
+<!-- INSTALL_DEVEL_IMG&#45;&gt;INSTALL_BUILD_IMG -->
+<g id="edge8" class="edge">
+<title>INSTALL_DEVEL_IMG&#45;&gt;INSTALL_BUILD_IMG</title>
+<path fill="none" stroke="black" d="M828,-330.34C828,-318.13 828,-303.29 828,-289.97"/>
+<polygon fill="black" stroke="black" points="831.5,-289.69 828,-279.69 824.5,-289.69 831.5,-289.69"/>
+</g>
+<!-- INSTALL_DEVEL_TAR -->
+<g id="node18" class="node">
+<title>INSTALL_DEVEL_TAR</title>
+<polygon fill="royalblue" stroke="black" points="656,-52 420,-52 420,-16 662,-16 662,-46 656,-52"/>
+<polyline fill="none" stroke="black" points="656,-52 656,-46 "/>
+<polyline fill="none" stroke="black" points="662,-46 656,-46 "/>
+<text text-anchor="middle" x="541" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_devel.tar</text>
+</g>
+<!-- INSTALL_DEVEL_IMG&#45;&gt;INSTALL_DEVEL_TAR -->
+<g id="edge18" class="edge">
+<title>INSTALL_DEVEL_IMG&#45;&gt;INSTALL_DEVEL_TAR</title>
+<path fill="none" stroke="red" d="M761.39,-330.48C708.37,-307.05 636.42,-266.99 595,-209.25 562.63,-164.12 549.27,-99.06 544.06,-62.54"/>
+<polygon fill="red" stroke="red" points="547.47,-61.64 542.7,-52.18 540.53,-62.55 547.47,-61.64"/>
+<text text-anchor="middle" x="708.5" y="-198.05" font-family="Times,serif" font-size="14.00">make save_amd64_install_devel</text>
+</g>
+<!-- INSTALL_TEST_IMG -->
+<g id="node12" class="node">
+<title>INSTALL_TEST_IMG</title>
+<path fill="royalblue" stroke="black" d="M948.5,-171.6C948.5,-174.24 893.15,-176.38 825,-176.38 756.85,-176.38 701.5,-174.24 701.5,-171.6 701.5,-171.6 701.5,-128.65 701.5,-128.65 701.5,-126.01 756.85,-123.87 825,-123.87 893.15,-123.87 948.5,-126.01 948.5,-128.65 948.5,-128.65 948.5,-171.6 948.5,-171.6"/>
+<path fill="none" stroke="black" d="M948.5,-171.6C948.5,-168.97 893.15,-166.83 825,-166.83 756.85,-166.83 701.5,-168.97 701.5,-171.6"/>
+<text text-anchor="middle" x="825" y="-153.93" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_test</text>
+<text text-anchor="middle" x="825" y="-138.93" font-family="Times,serif" font-size="14.00">install_test</text>
+</g>
+<!-- INSTALL_BUILD_IMG&#45;&gt;INSTALL_TEST_IMG -->
+<g id="edge9" class="edge">
+<title>INSTALL_BUILD_IMG&#45;&gt;INSTALL_TEST_IMG</title>
+<path fill="none" stroke="black" d="M827.25,-227.09C826.89,-214.88 826.45,-200.04 826.05,-186.72"/>
+<polygon fill="black" stroke="black" points="829.54,-186.33 825.75,-176.44 822.55,-186.54 829.54,-186.33"/>
+</g>
+<!-- INSTALL_BUILD_TAR -->
+<g id="node19" class="node">
+<title>INSTALL_BUILD_TAR</title>
+<polygon fill="royalblue" stroke="black" points="1162.5,-52 929.5,-52 929.5,-16 1168.5,-16 1168.5,-46 1162.5,-52"/>
+<polyline fill="none" stroke="black" points="1162.5,-52 1162.5,-46 "/>
+<polyline fill="none" stroke="black" points="1168.5,-46 1162.5,-46 "/>
+<text text-anchor="middle" x="1049" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_build.tar</text>
+</g>
+<!-- INSTALL_BUILD_IMG&#45;&gt;INSTALL_BUILD_TAR -->
+<g id="edge19" class="edge">
+<title>INSTALL_BUILD_IMG&#45;&gt;INSTALL_BUILD_TAR</title>
+<path fill="none" stroke="red" d="M882.76,-227.11C907.43,-214.09 935.91,-196.66 958,-176.25 994.16,-142.85 1022.19,-92.11 1037.09,-61.41"/>
+<polygon fill="red" stroke="red" points="1040.34,-62.72 1041.46,-52.19 1034.02,-59.72 1040.34,-62.72"/>
+<text text-anchor="middle" x="1117.5" y="-146.43" font-family="Times,serif" font-size="14.00">make save_amd64_install_build</text>
+</g>
+<!-- INSTALL_TEST_TAR -->
+<g id="node20" class="node">
+<title>INSTALL_TEST_TAR</title>
+<polygon fill="royalblue" stroke="black" points="905.5,-52 680.5,-52 680.5,-16 911.5,-16 911.5,-46 905.5,-52"/>
+<polyline fill="none" stroke="black" points="905.5,-52 905.5,-46 "/>
+<polyline fill="none" stroke="black" points="911.5,-46 905.5,-46 "/>
+<text text-anchor="middle" x="796" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_test.tar</text>
+</g>
+<!-- INSTALL_TEST_IMG&#45;&gt;INSTALL_TEST_TAR -->
+<g id="edge20" class="edge">
+<title>INSTALL_TEST_IMG&#45;&gt;INSTALL_TEST_TAR</title>
+<path fill="none" stroke="red" d="M799.99,-123.98C795.9,-118.47 792.26,-112.36 790,-106 785.07,-92.11 786.03,-75.77 788.46,-62.27"/>
+<polygon fill="red" stroke="red" points="791.96,-62.66 790.65,-52.15 785.12,-61.19 791.96,-62.66"/>
+<text text-anchor="middle" x="898.5" y="-94.8" font-family="Times,serif" font-size="14.00">make save_amd64_install_test</text>
+</g>
+</g>
+</svg>
diff --git a/ci/doc/generate_image.sh b/ci/doc/generate_image.sh
new file mode 100755
index 0000000..15f1774
--- /dev/null
+++ b/ci/doc/generate_image.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -ex
+
+rm -f ./*.svg ./*.png
+for i in *.dot; do
+  plantuml -Tsvg "$i";
+done
diff --git a/ci/docker/amd64/Dockerfile b/ci/docker/amd64/Dockerfile
new file mode 100644
index 0000000..9b25e28
--- /dev/null
+++ b/ci/docker/amd64/Dockerfile
@@ -0,0 +1,48 @@
+# Create a virtual environment with all tools installed
+# ref: https://hub.docker.com/_/ubuntu
+FROM ubuntu:latest AS env
+LABEL maintainer="[email protected]"
+# Install system build dependencies
+ENV PATH=/usr/local/bin:$PATH
+RUN apt-get update -qq \
+&& DEBIAN_FRONTEND=noninteractive apt-get install -yq git wget libssl-dev build-essential \
+ ninja-build python3 pkgconf libglib2.0-dev \
+&& apt-get clean \
+&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+ENTRYPOINT ["/usr/bin/bash", "-c"]
+CMD ["/usr/bin/bash"]
+
+# Install CMake 3.21.3
+RUN wget "https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.sh" \
+&& chmod a+x cmake-3.21.3-linux-x86_64.sh \
+&& ./cmake-3.21.3-linux-x86_64.sh --prefix=/usr/local/ --skip-license \
+&& rm cmake-3.21.3-linux-x86_64.sh
+
+FROM env AS devel
+WORKDIR /home/project
+COPY . .
+
+FROM devel AS build
+RUN cmake -version
+RUN cmake -S. -Bbuild
+RUN cmake --build build --target all -v
+RUN cmake --build build --target install -v
+
+FROM build AS test
+ENV CTEST_OUTPUT_ON_FAILURE=1
+RUN cmake --build build --target test -v
+
+# Test install rules
+FROM env AS install_env
+COPY --from=build /usr/local /usr/local/
+
+FROM install_env AS install_devel
+WORKDIR /home/sample
+COPY ci/sample .
+
+FROM install_devel AS install_build
+RUN cmake -S. -Bbuild
+RUN cmake --build build --target all -v
+
+FROM install_build AS install_test
+RUN cmake --build build --target test
diff --git a/ci/docker/toolchain/Dockerfile b/ci/docker/toolchain/Dockerfile
new file mode 100644
index 0000000..1bf25ed
--- /dev/null
+++ b/ci/docker/toolchain/Dockerfile
@@ -0,0 +1,34 @@
+# Create a virtual environment with all tools installed
+# ref: https://hub.docker.com/_/ubuntu
+FROM ubuntu:latest AS env
+LABEL maintainer="[email protected]"
+# Install system build dependencies
+ENV PATH=/usr/local/bin:$PATH
+RUN apt-get update -qq \
+&& DEBIAN_FRONTEND=noninteractive apt-get install -yq git wget libssl-dev build-essential \
+ ninja-build python3 pkgconf libglib2.0-dev \
+&& apt-get clean \
+&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+ENTRYPOINT ["/usr/bin/bash", "-c"]
+CMD ["/usr/bin/bash"]
+
+# Install CMake 3.21.3
+RUN wget "https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.sh" \
+&& chmod a+x cmake-3.21.3-linux-x86_64.sh \
+&& ./cmake-3.21.3-linux-x86_64.sh --prefix=/usr/local/ --skip-license \
+&& rm cmake-3.21.3-linux-x86_64.sh
+
+FROM env AS devel
+WORKDIR /home/project
+COPY . .
+
+ARG TARGET
+ENV TARGET ${TARGET:-unknown}
+
+FROM devel AS build
+RUN cmake -version
+RUN ./scripts/run_integration.sh build
+
+FROM build AS test
+RUN ./scripts/run_integration.sh qemu
+RUN ./scripts/run_integration.sh test
diff --git a/ci/sample/CMakeLists.txt b/ci/sample/CMakeLists.txt
new file mode 100644
index 0000000..b60e92f
--- /dev/null
+++ b/ci/sample/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.15)
+project(Sample VERSION 1.0.0 LANGUAGES CXX)
+
+include(CTest)
+find_package(CpuFeatures REQUIRED)
+
+add_executable(sample main.cpp)
+target_compile_features(sample PUBLIC cxx_std_11)
+set_target_properties(sample PROPERTIES
+  CXX_STANDARD 11
+  CXX_STANDARD_REQUIRED ON
+  VERSION ${PROJECT_VERSION})
+target_link_libraries(sample PRIVATE CpuFeatures::cpu_features)
+
+if(BUILD_TESTING)
+  add_test(NAME sample_test COMMAND sample)
+endif()
+
+include(GNUInstallDirs)
+install(TARGETS sample
+  EXPORT SampleTargets
+  DESTINATION ${CMAKE_INSTALL_BIN_DIR})
diff --git a/ci/sample/main.cpp b/ci/sample/main.cpp
new file mode 100644
index 0000000..45ec651
--- /dev/null
+++ b/ci/sample/main.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+
+#include "cpuinfo_x86.h"
+
+using namespace cpu_features;
+
+int main(int /*argc*/, char** /*argv*/) {
+  static const X86Features features = GetX86Info().features;
+  std::cout << std::endl;
+  return 0;
+}
diff --git a/ci/vagrant/freebsd/Vagrantfile b/ci/vagrant/freebsd/Vagrantfile
new file mode 100644
index 0000000..7ef7bfa
--- /dev/null
+++ b/ci/vagrant/freebsd/Vagrantfile
@@ -0,0 +1,102 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# All Vagrant configuration is done below. The "2" in Vagrant.configure
+# configures the configuration version (we support older styles for
+# backwards compatibility). Please don't change it unless you know what
+# you're doing.
+Vagrant.configure("2") do |config|
+  # The most common configuration options are documented and commented below.
+  # For a complete reference, please see the online documentation at
+  # https://docs.vagrantup.com.
+
+  # Every Vagrant development environment requires a box. You can search for
+  # boxes at https://vagrantcloud.com/search.
+  config.vm.guest = :freebsd
+  config.vm.box = "generic/freebsd12"
+
+  config.ssh.shell = "sh"
+
+  # Disable automatic box update checking. If you disable this, then
+  # boxes will only be checked for updates when the user runs
+  # `vagrant box outdated`. This is not recommended.
+  # config.vm.box_check_update = false
+
+  # Create a forwarded port mapping which allows access to a specific port
+  # within the machine from a port on the host machine. In the example below,
+  # accessing "localhost:8080" will access port 80 on the guest machine.
+  # NOTE: This will enable public access to the opened port
+  # config.vm.network "forwarded_port", guest: 80, host: 8080
+
+  # Create a forwarded port mapping which allows access to a specific port
+  # within the machine from a port on the host machine and only allow access
+  # via 127.0.0.1 to disable public access
+  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
+
+  # Create a private network, which allows host-only access to the machine
+  # using a specific IP.
+  # config.vm.network "private_network", ip: "192.168.33.10"
+
+  # Create a public network, which generally matched to bridged network.
+  # Bridged networks make the machine appear as another physical device on
+  # your network.
+  # config.vm.network "public_network"
+
+  # Share an additional folder to the guest VM. The first argument is
+  # the path on the host to the actual folder. The second argument is
+  # the path on the guest to mount the folder. And the optional third
+  # argument is a set of non-required options.
+  #config.vm.synced_folder "../../..", "/home/vagrant/project"
+  config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true
+
+  config.vm.provision "file", source: "../../../CMakeLists.txt", destination: "$HOME/project/"
+  config.vm.provision "file", source: "../../../cmake", destination: "$HOME/project/"
+  config.vm.provision "file", source: "../../../include", destination: "$HOME/project/"
+  config.vm.provision "file", source: "../../../src", destination: "$HOME/project/"
+  config.vm.provision "file", source: "../../../test", destination: "$HOME/project/"
+
+  # Provider-specific configuration so you can fine-tune various
+  # backing providers for Vagrant. These expose provider-specific options.
+  # Example for VirtualBox:
+  #
+  # config.vm.provider "virtualbox" do |vb|
+  #   # Display the VirtualBox GUI when booting the machine
+  #   vb.gui = true
+  #
+  #   # Customize the amount of memory on the VM:
+  #   vb.memory = "1024"
+  # end
+  #
+  # View the documentation for the provider you are using for more
+  # information on available options.
+
+  # Enable provisioning with a shell script. Additional provisioners such as
+  # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
+  # documentation for more information about their specific syntax and use.
+  # note: clang installed by default
+  config.vm.provision "env", type: "shell", inline:<<-SHELL
+  set -x
+  pkg update -f
+  pkg install -y git cmake
+  SHELL
+  config.vm.provision "devel", type: "shell", inline:<<-SHELL
+  set -x
+  cd project
+  ls
+  SHELL
+  config.vm.provision "configure", type: "shell", inline:<<-SHELL
+  set -x
+  cd project
+  cmake -S. -Bbuild -DBUILD_TESTING=ON
+  SHELL
+  config.vm.provision "build", type: "shell", inline:<<-SHELL
+  set -x
+  cd project
+  cmake --build build -v
+  SHELL
+  config.vm.provision "test", type: "shell", inline:<<-SHELL
+  set -x
+  cd project
+  cmake --build build --target test -v
+  SHELL
+end
diff --git a/cmake/README.md b/cmake/README.md
index b6baeaa..b2d96c4 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -2,27 +2,29 @@
 
 ## Recommended usage : Incorporating cpu_features into a CMake project
 
-  For API / ABI compatibility reasons, it is recommended to build and use
-  cpu_features in a subdirectory of your project or as an embedded dependency.
+For API / ABI compatibility reasons, it is recommended to build and use
+cpu_features in a subdirectory of your project or as an embedded dependency.
 
-  This is similar to the recommended usage of the googletest framework
-  ( https://github.com/google/googletest/blob/master/googletest/README.md )
+This is similar to the recommended usage of the googletest framework
+( https://github.com/google/googletest/blob/main/googletest/README.md )
 
-  Build and use step-by-step
+Build and use step-by-step
 
 
-  1- Download cpu_features and copy it in a sub-directory in your project.
-      or add cpu_features as a git-submodule in your project
+1- Download cpu_features and copy it in a sub-directory in your project.
+or add cpu_features as a git-submodule in your project
 
-  2- You can then use the cmake command `add_subdirectory()` to include
-     cpu_features directly and use the `cpu_features` target in your project.
+2- You can then use the cmake command `add_subdirectory()` to include
+cpu_features directly and use the `cpu_features` target in your project.
 
-  3- Add the `cpu_features` target to the `target_link_libraries()` section of
-     your executable or of your library.
+3- Add the `cpu_features` target to the `target_link_libraries()` section of
+your executable or of your library.
 
-## Enabling tests
+## Disabling tests
 
-  CMake default options for cpu_features is Release built type with tests
-  disabled. To enable testing set cmake `BUILD_TESTING` variable to `ON`,
-  [.travis.yml](../.travis.yml) and [appveyor.yml](../appveyor.yml) have up to
-  date examples.
+CMake default options for cpu_features is `Release` built type with tests
+enabled. To disable testing set cmake `BUILD_TESTING` variable to `OFF`.
+e.g.
+```sh
+cmake -S. -Bbuild -DBUILD_TESTING=OFF
+```
diff --git a/cmake/googletest.CMakeLists.txt.in b/cmake/googletest.CMakeLists.txt.in
index d60a33e..8003c2c 100644
--- a/cmake/googletest.CMakeLists.txt.in
+++ b/cmake/googletest.CMakeLists.txt.in
@@ -5,7 +5,7 @@
 include(ExternalProject)
 ExternalProject_Add(googletest
   GIT_REPOSITORY    https://github.com/google/googletest.git
-  GIT_TAG           master
+  GIT_TAG           main
   SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
   BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
   CONFIGURE_COMMAND ""
diff --git a/include/cpu_features_macros.h b/include/cpu_features_macros.h
index 4b231a1..6a2f76a 100644
--- a/include/cpu_features_macros.h
+++ b/include/cpu_features_macros.h
@@ -67,20 +67,33 @@
 // Os
 ////////////////////////////////////////////////////////////////////////////////
 
-#if defined(__linux__)
-#define CPU_FEATURES_OS_LINUX_OR_ANDROID
+#if (defined(__freebsd__) || defined(__FreeBSD__))
+#define CPU_FEATURES_OS_FREEBSD
 #endif
 
 #if defined(__ANDROID__)
 #define CPU_FEATURES_OS_ANDROID
 #endif
 
+#if defined(__linux__) && !defined(CPU_FEATURES_OS_FREEBSD) && \
+    !defined(CPU_FEATURES_OS_ANDROID)
+#define CPU_FEATURES_OS_LINUX
+#endif
+
 #if (defined(_WIN64) || defined(_WIN32))
 #define CPU_FEATURES_OS_WINDOWS
 #endif
 
 #if (defined(__apple__) || defined(__APPLE__) || defined(__MACH__))
-#define CPU_FEATURES_OS_DARWIN
+// From https://stackoverflow.com/a/49560690
+#include "TargetConditionals.h"
+#if defined(TARGET_OS_OSX)
+#define CPU_FEATURES_OS_MACOS
+#endif
+#if defined(TARGET_OS_IPHONE)
+// This is set for any non-Mac Apple products (IOS, TV, WATCH)
+#define CPU_FEATURES_OS_IPHONE
+#endif
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -213,4 +226,24 @@
 #endif  //  defined(__mips_msa)
 #endif  //  defined(CPU_FEATURES_ARCH_MIPS)
 
+////////////////////////////////////////////////////////////////////////////////
+// Utils
+////////////////////////////////////////////////////////////////////////////////
+
+// Communicates to the compiler that the block is unreachable
+#if defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
+#define CPU_FEATURES_UNREACHABLE() __builtin_unreachable()
+#elif defined(CPU_FEATURES_COMPILER_MSC)
+#define CPU_FEATURES_UNREACHABLE() __assume(0)
+#else
+#define CPU_FEATURES_UNREACHABLE()
+#endif
+
+// Communicates to the compiler that the function is now deprecated
+#if defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
+#define CPU_FEATURES_DEPRECATED(message) __attribute__((deprecated(message)))
+#else
+#define CPU_FEATURES_DEPRECATED(message)
+#endif
+
 #endif  // CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
diff --git a/include/cpuinfo_aarch64.h b/include/cpuinfo_aarch64.h
index d85d46d..1b57d21 100644
--- a/include/cpuinfo_aarch64.h
+++ b/include/cpuinfo_aarch64.h
@@ -71,6 +71,7 @@
   int dgh : 1;         // Data Gathering Hint instruction.
   int rng : 1;         // True random number generator support.
   int bti : 1;         // Branch target identification.
+  int mte : 1;         // Memory tagging extension.
 
   // Make sure to update Aarch64FeaturesEnum below if you add a field here.
 } Aarch64Features;
@@ -139,6 +140,7 @@
   AARCH64_DGH,
   AARCH64_RNG,
   AARCH64_BTI,
+  AARCH64_MTE,
   AARCH64_LAST_,
 } Aarch64FeaturesEnum;
 
diff --git a/include/cpuinfo_ppc.h b/include/cpuinfo_ppc.h
index f691194..da3e696 100644
--- a/include/cpuinfo_ppc.h
+++ b/include/cpuinfo_ppc.h
@@ -17,7 +17,6 @@
 
 #include "cpu_features_cache_info.h"
 #include "cpu_features_macros.h"
-#include "internal/hwcaps.h"
 
 CPU_FEATURES_START_CPP_NAMESPACE
 
@@ -71,15 +70,19 @@
   PPCFeatures features;
 } PPCInfo;
 
-// This function is guaranteed to be malloc, memset and memcpy free.
 PPCInfo GetPPCInfo(void);
 
 typedef struct {
+  char platform[64];       // 0 terminated string
+  char base_platform[64];  // 0 terminated string
+} PPCPlatformTypeStrings;
+
+typedef struct {
   char platform[64];  // 0 terminated string
   char model[64];     // 0 terminated string
   char machine[64];   // 0 terminated string
   char cpu[64];       // 0 terminated string
-  PlatformType type;
+  PPCPlatformTypeStrings type;
 } PPCPlatformStrings;
 
 PPCPlatformStrings GetPPCPlatformStrings(void);
diff --git a/include/cpuinfo_x86.h b/include/cpuinfo_x86.h
index 8d40f71..88daca4 100644
--- a/include/cpuinfo_x86.h
+++ b/include/cpuinfo_x86.h
@@ -21,6 +21,13 @@
 
 CPU_FEATURES_START_CPP_NAMESPACE
 
+// CPUID Vendors
+#define CPU_FEATURES_VENDOR_GENUINE_INTEL "GenuineIntel"
+#define CPU_FEATURES_VENDOR_AUTHENTIC_AMD "AuthenticAMD"
+#define CPU_FEATURES_VENDOR_HYGON_GENUINE "HygonGenuine"
+#define CPU_FEATURES_VENDOR_CENTAUR_HAULS "CentaurHauls"
+#define CPU_FEATURES_VENDOR_SHANGHAI "  Shanghai  "
+
 // See https://en.wikipedia.org/wiki/CPUID for a list of x86 cpu features.
 // The field names are based on the short name provided in the wikipedia tables.
 typedef struct {
@@ -89,6 +96,7 @@
 
   int dca : 1;
   int ss : 1;
+  int adx : 1;
   // Make sure to update X86FeaturesEnum below if you add a field here.
 } X86Features;
 
@@ -97,46 +105,69 @@
   int family;
   int model;
   int stepping;
-  char vendor[13];  // 0 terminated string
+  char vendor[13];        // 0 terminated string
+  char brand_string[49];  // 0 terminated string
 } X86Info;
 
 // Calls cpuid and returns an initialized X86info.
-// This function is guaranteed to be malloc, memset and memcpy free.
 X86Info GetX86Info(void);
 
 // Returns cache hierarchy informations.
 // Can call cpuid multiple times.
 // Only works on Intel CPU at the moment.
-// This function is guaranteed to be malloc, memset and memcpy free.
 CacheInfo GetX86CacheInfo(void);
 
 typedef enum {
   X86_UNKNOWN,
-  INTEL_CORE,      // CORE
-  INTEL_PNR,       // PENRYN
-  INTEL_NHM,       // NEHALEM
-  INTEL_ATOM_BNL,  // BONNELL
-  INTEL_WSM,       // WESTMERE
-  INTEL_SNB,       // SANDYBRIDGE
-  INTEL_IVB,       // IVYBRIDGE
-  INTEL_ATOM_SMT,  // SILVERMONT
-  INTEL_HSW,       // HASWELL
-  INTEL_BDW,       // BROADWELL
-  INTEL_SKL,       // SKYLAKE
-  INTEL_ATOM_GMT,  // GOLDMONT
-  INTEL_KBL,       // KABY LAKE
-  INTEL_CFL,       // COFFEE LAKE
-  INTEL_WHL,       // WHISKEY LAKE
-  INTEL_CNL,       // CANNON LAKE
-  INTEL_ICL,       // ICE LAKE
-  INTEL_TGL,       // TIGER LAKE
-  INTEL_SPR,       // SAPPHIRE RAPIDS
-  AMD_HAMMER,      // K8
-  AMD_K10,         // K10
-  AMD_BOBCAT,      // K14
-  AMD_BULLDOZER,   // K15
-  AMD_JAGUAR,      // K16
-  AMD_ZEN,         // K17
+  ZHAOXIN_ZHANGJIANG,  // ZhangJiang
+  ZHAOXIN_WUDAOKOU,    // WuDaoKou
+  ZHAOXIN_LUJIAZUI,    // LuJiaZui
+  ZHAOXIN_YONGFENG,    // YongFeng
+  INTEL_80486,         // 80486
+  INTEL_P5,            // P5
+  INTEL_LAKEMONT,      // LAKEMONT
+  INTEL_CORE,          // CORE
+  INTEL_PNR,           // PENRYN
+  INTEL_NHM,           // NEHALEM
+  INTEL_ATOM_BNL,      // BONNELL
+  INTEL_WSM,           // WESTMERE
+  INTEL_SNB,           // SANDYBRIDGE
+  INTEL_IVB,           // IVYBRIDGE
+  INTEL_ATOM_SMT,      // SILVERMONT
+  INTEL_HSW,           // HASWELL
+  INTEL_BDW,           // BROADWELL
+  INTEL_SKL,           // SKYLAKE
+  INTEL_ATOM_GMT,      // GOLDMONT
+  INTEL_KBL,           // KABY LAKE
+  INTEL_CFL,           // COFFEE LAKE
+  INTEL_WHL,           // WHISKEY LAKE
+  INTEL_CNL,           // CANNON LAKE
+  INTEL_ICL,           // ICE LAKE
+  INTEL_TGL,           // TIGER LAKE
+  INTEL_SPR,           // SAPPHIRE RAPIDS
+  INTEL_ADL,           // ALDER LAKE
+  INTEL_RCL,           // ROCKET LAKE
+  INTEL_KNIGHTS_M,     // KNIGHTS MILL
+  INTEL_KNIGHTS_L,     // KNIGHTS LANDING
+  INTEL_KNIGHTS_F,     // KNIGHTS FERRY
+  INTEL_KNIGHTS_C,     // KNIGHTS CORNER
+  INTEL_NETBURST,      // NETBURST
+  AMD_HAMMER,          // K8  HAMMER
+  AMD_K10,             // K10
+  AMD_K11,             // K11
+  AMD_K12,             // K12
+  AMD_BOBCAT,          // K14 BOBCAT
+  AMD_PILEDRIVER,      // K15 PILEDRIVER
+  AMD_STREAMROLLER,    // K15 STREAMROLLER
+  AMD_EXCAVATOR,       // K15 EXCAVATOR
+  AMD_BULLDOZER,       // K15 BULLDOZER
+  AMD_JAGUAR,          // K16 JAGUAR
+  AMD_PUMA,            // K16 PUMA
+  AMD_ZEN,             // K17 ZEN
+  AMD_ZEN_PLUS,        // K17 ZEN+
+  AMD_ZEN2,            // K17 ZEN 2
+  AMD_ZEN3,            // K19 ZEN 3
+  X86_MICROARCHITECTURE_LAST_,
 } X86Microarchitecture;
 
 // Returns the underlying microarchitecture by looking at X86Info's vendor,
@@ -146,7 +177,7 @@
 // Calls cpuid and fills the brand_string.
 // - brand_string *must* be of size 49 (beware of array decaying).
 // - brand_string will be zero terminated.
-// - This function calls memcpy.
+CPU_FEATURES_DEPRECATED("brand_string is now embedded in X86Info by default")
 void FillX86BrandString(char brand_string[49]);
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -213,6 +244,7 @@
   X86_RDRND,
   X86_DCA,
   X86_SS,
+  X86_ADX,
   X86_LAST_,
 } X86FeaturesEnum;
 
diff --git a/include/internal/hwcaps.h b/include/internal/hwcaps.h
index 62037c8..d7fc782 100644
--- a/include/internal/hwcaps.h
+++ b/include/internal/hwcaps.h
@@ -79,6 +79,7 @@
 #define AARCH64_HWCAP2_DGH (1UL << 15)
 #define AARCH64_HWCAP2_RNG (1UL << 16)
 #define AARCH64_HWCAP2_BTI (1UL << 17)
+#define AARCH64_HWCAP2_MTE (1UL << 18)
 
 // http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/uapi/asm/hwcap.h
 #define ARM_HWCAP_SWP (1UL << 0)
@@ -170,16 +171,19 @@
   unsigned long hwcaps2;
 } HardwareCapabilities;
 
+// Retrieves values from auxiliary vector for types AT_HWCAP and AT_HWCAP2.
+// First tries to call getauxval(), if not available falls back to reading
+// "/proc/self/auxv".
 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
+
+// Checks whether value for AT_HWCAP (or AT_HWCAP2) match hwcaps_mask.
 bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
                              const HardwareCapabilities hwcaps);
 
-typedef struct {
-  char platform[64];       // 0 terminated string
-  char base_platform[64];  // 0 terminated string
-} PlatformType;
-
-PlatformType CpuFeatures_GetPlatformType(void);
+// Get pointer for the AT_PLATFORM type.
+const char* CpuFeatures_GetPlatformPointer(void);
+// Get pointer for the AT_BASE_PLATFORM type.
+const char* CpuFeatures_GetBasePlatformPointer(void);
 
 CPU_FEATURES_END_CPP_NAMESPACE
 
diff --git a/include/internal/string_view.h b/include/internal/string_view.h
index 64fed40..a109d45 100644
--- a/include/internal/string_view.h
+++ b/include/internal/string_view.h
@@ -96,7 +96,8 @@
 
 // Checks if line contains the specified whitespace separated word.
 bool CpuFeatures_StringView_HasWord(const StringView line,
-                                    const char* const word);
+                                    const char* const word,
+                                    const char separator);
 
 // Get key/value from line. key and value are separated by ": ".
 // key and value are cleaned up from leading and trailing whitespaces.
diff --git a/ndk_compat/CMakeLists.txt b/ndk_compat/CMakeLists.txt
index 186708a..37b3866 100644
--- a/ndk_compat/CMakeLists.txt
+++ b/ndk_compat/CMakeLists.txt
@@ -54,7 +54,7 @@
 #
 # program : NDK compat test program
 #
-if(ENABLE_TESTING)
+if(BUILD_TESTING)
   add_executable(ndk-compat-test ndk-compat-test.c)
   target_link_libraries(ndk-compat-test PRIVATE ndk_compat)
 endif()
diff --git a/scripts/make_release.sh b/scripts/make_release.sh
new file mode 100755
index 0000000..01e85f7
--- /dev/null
+++ b/scripts/make_release.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+
+set -e # Fail on error
+set -u # Treat unset variables as an error and exit immediately
+
+ACTION='\033[1;90m'
+FINISHED='\033[1;96m'
+NOCOLOR='\033[0m'
+ERROR='\033[0;31m'
+
+echo -e "${ACTION}Checking environnement${NOCOLOR}"
+if [[ ! $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
+then
+    echo -e "${ERROR}Invalid version number. Aborting. ${NOCOLOR}"
+    exit 1
+fi
+
+declare -r VERSION=$1
+declare -r GIT_TAG="v$1"
+
+BRANCH=$(git rev-parse --abbrev-ref HEAD)
+if [[ "${BRANCH}" != "main" ]]
+then
+    echo -e "${ERROR}Not on main. Aborting. ${NOCOLOR}"
+    echo
+    exit 1
+fi
+
+git fetch
+HEADHASH=$(git rev-parse HEAD)
+UPSTREAMHASH=$(git rev-parse main@{upstream})
+
+if [[ "${HEADHASH}" != "${UPSTREAMHASH}" ]]
+then
+    echo -e "${ERROR}Not up to date with origin. Aborting.${NOCOLOR}"
+    echo
+    exit 1
+fi
+
+git update-index -q --refresh
+if ! git diff-index --quiet HEAD --
+then
+    echo -e "${ERROR}Branch has uncommited changes. Aborting.${NOCOLOR}"
+    exit 1
+fi
+
+if [ ! -z "$(git ls-files --exclude-standard --others)" ]
+then
+    echo -e "${ERROR}Branch has untracked files. Aborting.${NOCOLOR}"
+    exit 1
+fi
+
+declare -r LATEST_GIT_TAG=$(git describe --tags --abbrev=0)
+declare -r LATEST_VERSION=${LATEST_GIT_TAG#"v"}
+
+if ! dpkg --compare-versions "${VERSION}" "gt" "${LATEST_VERSION}"
+then
+    echo -e "${ERROR}Invalid version ${VERSION} <= ${LATEST_VERSION} (latest). Aborting.${NOCOLOR}"
+    exit 1
+fi
+
+echo -e "${ACTION}Modifying CMakeLists.txt${NOCOLOR}"
+sed -i "s/CpuFeatures VERSION ${LATEST_VERSION}/CpuFeatures VERSION ${VERSION}/g" CMakeLists.txt
+
+echo -e "${ACTION}Commit new revision${NOCOLOR}"
+git add CMakeLists.txt
+git commit -m"Release ${GIT_TAG}"
+
+echo -e "${ACTION}Create new tag${NOCOLOR}"
+git tag ${GIT_TAG}
+
+echo -e "${FINISHED}Local release is ready. Run `git push origin --tags`${NOCOLOR}"
diff --git a/scripts/run_integration.sh b/scripts/run_integration.sh
index fd88d60..645cb6a 100755
--- a/scripts/run_integration.sh
+++ b/scripts/run_integration.sh
@@ -1,209 +1,363 @@
 #!/usr/bin/env bash
-
-readonly SCRIPT_FOLDER=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
-readonly PROJECT_FOLDER="${SCRIPT_FOLDER}/.."
-readonly ARCHIVE_FOLDER=~/cpu_features_archives
-readonly QEMU_INSTALL=${ARCHIVE_FOLDER}/qemu
-readonly DEFAULT_CMAKE_ARGS=" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON"
+set -eo pipefail
 
 function extract() {
+  echo "Extracting ${1}..."
   case $1 in
     *.tar.bz2)   tar xjf "$1"    ;;
     *.tar.xz)    tar xJf "$1"    ;;
     *.tar.gz)    tar xzf "$1"    ;;
     *)
-      echo "don't know how to extract '$1'..."
+      >&2 echo "don't know how to extract '$1'..."
       exit 1
   esac
 }
 
-function unpackifnotexists() {
-  mkdir -p "${ARCHIVE_FOLDER}"
-  cd "${ARCHIVE_FOLDER}" || exit
-  local URL=$1
-  local RELATIVE_FOLDER=$2
-  local DESTINATION="${ARCHIVE_FOLDER}/${RELATIVE_FOLDER}"
+function unpack() {
+  mkdir -p "${ARCHIVE_DIR}"
+  cd "${ARCHIVE_DIR}" || exit 2
+  local -r URL=$1
+  local -r RELATIVE_DIR=$2
+  local -r DESTINATION="${ARCHIVE_DIR}/${RELATIVE_DIR}"
   if [[  ! -d "${DESTINATION}" ]] ; then
-    local ARCHIVE_NAME=$(echo ${URL} | sed 's/.*\///')
-    test -f "${ARCHIVE_NAME}" || wget -q "${URL}"
+    echo "Downloading ${URL}..."
+    local -r ARCHIVE_NAME=$(basename "${URL}")
+    test -f "${ARCHIVE_NAME}" || wget --no-verbose "${URL}"
     extract "${ARCHIVE_NAME}"
     rm -f "${ARCHIVE_NAME}"
   fi
 }
 
-function installqemuifneeded() {
-  local VERSION=${QEMU_VERSION:=2.11.1}
-  local ARCHES=${QEMU_ARCHES:=arm aarch64 i386 x86_64 mips mipsel mips64 mips64el}
-  local TARGETS=${QEMU_TARGETS:=$(echo "$ARCHES" | sed 's#$# #;s#\([^ ]*\) #\1-linux-user #g')}
+function install_qemu() {
+  if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then
+    >&2 echo 'QEMU is disabled !'
+    return 0
+  fi
+  local -r QEMU_VERSION=${QEMU_VERSION:=5.2.0}
+  local -r QEMU_TARGET=${QEMU_ARCH}-linux-user
 
-  if echo "${VERSION} ${TARGETS}" | cmp --silent ${QEMU_INSTALL}/.build -; then
-    echo "qemu ${VERSION} up to date!"
+  if echo "${QEMU_VERSION} ${QEMU_TARGET}" | cmp --silent "${QEMU_INSTALL}/.build" -; then
+    echo "qemu ${QEMU_VERSION} up to date!"
     return 0
   fi
 
-  echo "VERSION: ${VERSION}"
-  echo "TARGETS: ${TARGETS}"
+  echo "QEMU_VERSION: ${QEMU_VERSION}"
+  echo "QEMU_TARGET: ${QEMU_TARGET}"
 
-  rm -rf ${QEMU_INSTALL}
+  rm -rf "${QEMU_INSTALL}"
 
   # Checking for a tarball before downloading makes testing easier :-)
-  local QEMU_URL="http://wiki.qemu-project.org/download/qemu-${VERSION}.tar.xz"
-  local QEMU_FOLDER="qemu-${VERSION}"
-  unpackifnotexists ${QEMU_URL} ${QEMU_FOLDER}
-  cd ${QEMU_FOLDER} || exit
+  local -r QEMU_URL="http://wiki.qemu-project.org/download/qemu-${QEMU_VERSION}.tar.xz"
+  local -r QEMU_DIR="qemu-${QEMU_VERSION}"
+  unpack ${QEMU_URL} ${QEMU_DIR}
+  cd ${QEMU_DIR} || exit 2
 
+  # Qemu (meson based build) depends on: pkgconf, libglib2.0, python3, ninja
   ./configure \
     --prefix="${QEMU_INSTALL}" \
-    --target-list="${TARGETS}" \
-    --disable-docs \
-    --disable-sdl \
-    --disable-gtk \
-    --disable-gnutls \
-    --disable-gcrypt \
-    --disable-nettle \
+    --target-list="${QEMU_TARGET}" \
+    --audio-drv-list= \
+    --disable-brlapi \
+    --disable-curl \
     --disable-curses \
-    --static
+    --disable-docs \
+    --disable-gcrypt \
+    --disable-gnutls \
+    --disable-gtk \
+    --disable-libnfs \
+    --disable-libssh \
+    --disable-nettle \
+    --disable-opengl \
+    --disable-sdl \
+    --disable-virglrenderer \
+    --disable-vte \
+    --enable-modules
 
-  make -j4
+  # --static Not supported on Archlinux
+  # so we use --enable-modules
+
+  # wrapper on ninja
+  make -j8
   make install
 
-  echo "$VERSION $TARGETS" > ${QEMU_INSTALL}/.build
+  echo "$QEMU_VERSION $QEMU_TARGET" > "${QEMU_INSTALL}/.build"
 }
 
 function assert_defined(){
-  local VALUE=${1}
-  : "${VALUE?"${1} needs to be defined"}"
+  if [[ -z "${!1}" ]]; then
+    >&2 echo "Variable '${1}' must be defined"
+    exit 1
+  fi
 }
 
-function integrate() {
-  cd "${PROJECT_FOLDER}"
-  case "${OS}" in
-   "Windows_NT") CMAKE_BUILD_ARGS="--config Debug --target ALL_BUILD"
-                 CMAKE_TEST_FILES="${BUILD_DIR}/test/Debug/*_test.exe"
-                 DEMO=${BUILD_DIR}/Debug/list_cpu_features.exe
-                 ;;
-   *)            CMAKE_BUILD_ARGS="--target all"
-                 CMAKE_TEST_FILES="${BUILD_DIR}/test/*_test"
-                 DEMO=${BUILD_DIR}/list_cpu_features
-                 ;;
-  esac
-
-  # Generating CMake configuration
-  cmake -H. -B"${BUILD_DIR}" ${DEFAULT_CMAKE_ARGS} "${CMAKE_ADDITIONAL_ARGS[@]}" -G"${CMAKE_GENERATOR:-Unix Makefiles}"
-
-  # Building
-  cmake --build "${BUILD_DIR}" ${CMAKE_BUILD_ARGS}
-
-  # Running tests if needed
-  if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then
-    return
-  fi
-  RUN_CMD=""
-  if [[ -n "${QEMU_ARCH}" ]]; then
-    installqemuifneeded
-    RUN_CMD="${QEMU_INSTALL}/bin/qemu-${QEMU_ARCH} ${QEMU_ARGS[@]}"
-  fi
-  for test_binary in ${CMAKE_TEST_FILES}; do
-    ${RUN_CMD} ${test_binary}
-  done
-  ${RUN_CMD} ${DEMO}
+function clean_build() {
+  # Cleanup previous build
+  rm -rf "${BUILD_DIR}"
+  mkdir -p "${BUILD_DIR}"
 }
 
 function expand_linaro_config() {
-  assert_defined TARGET
-  local LINARO_ROOT_URL=https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11
+  #ref: https://releases.linaro.org/components/toolchain/binaries/
+  local -r LINARO_VERSION=7.5-2019.12
+  local -r LINARO_ROOT_URL=https://releases.linaro.org/components/toolchain/binaries/${LINARO_VERSION}
 
-  local GCC_URL=${LINARO_ROOT_URL}/${TARGET}/gcc-linaro-7.2.1-2017.11-x86_64_${TARGET}.tar.xz
-  local GCC_RELATIVE_FOLDER="gcc-linaro-7.2.1-2017.11-x86_64_${TARGET}"
-  unpackifnotexists "${GCC_URL}" "${GCC_RELATIVE_FOLDER}"
+  local -r GCC_VERSION=7.5.0-2019.12
+  local -r GCC_URL=${LINARO_ROOT_URL}/${TARGET}/gcc-linaro-${GCC_VERSION}-x86_64_${TARGET}.tar.xz
+  local -r GCC_RELATIVE_DIR="gcc-linaro-${GCC_VERSION}-x86_64_${TARGET}"
+  unpack "${GCC_URL}" "${GCC_RELATIVE_DIR}"
 
-  local SYSROOT_URL=${LINARO_ROOT_URL}/${TARGET}/sysroot-glibc-linaro-2.25-2017.11-${TARGET}.tar.xz
-  local SYSROOT_RELATIVE_FOLDER=sysroot-glibc-linaro-2.25-2017.11-${TARGET}
-  unpackifnotexists "${SYSROOT_URL}" "${SYSROOT_RELATIVE_FOLDER}"
+  local -r SYSROOT_VERSION=2.25-2019.12
+  local -r SYSROOT_URL=${LINARO_ROOT_URL}/${TARGET}/sysroot-glibc-linaro-${SYSROOT_VERSION}-${TARGET}.tar.xz
+  local -r SYSROOT_RELATIVE_DIR=sysroot-glibc-linaro-${SYSROOT_VERSION}-${TARGET}
+  unpack "${SYSROOT_URL}" "${SYSROOT_RELATIVE_DIR}"
 
-  local SYSROOT_FOLDER=${ARCHIVE_FOLDER}/${SYSROOT_RELATIVE_FOLDER}
-  local GCC_FOLDER=${ARCHIVE_FOLDER}/${GCC_RELATIVE_FOLDER}
+  local -r SYSROOT_DIR=${ARCHIVE_DIR}/${SYSROOT_RELATIVE_DIR}
+  local -r STAGING_DIR=${ARCHIVE_DIR}/${SYSROOT_RELATIVE_DIR}-stage
+  local -r GCC_DIR=${ARCHIVE_DIR}/${GCC_RELATIVE_DIR}
 
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSTEM_NAME=Linux)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSTEM_PROCESSOR=${TARGET})
+  # Write a Toolchain file
+  # note: This is manadatory to use a file in order to have the CMake variable
+  # 'CMAKE_CROSSCOMPILING' set to TRUE.
+  # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux
+  cat >"$TOOLCHAIN_FILE" <<EOL
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR ${TARGET})
 
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSROOT=${SYSROOT_FOLDER})
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_C_COMPILER=${GCC_FOLDER}/bin/${TARGET}-gcc)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_CXX_COMPILER=${GCC_FOLDER}/bin/${TARGET}-g++)
+set(CMAKE_SYSROOT ${SYSROOT_DIR})
+set(CMAKE_STAGING_PREFIX ${STAGING_DIR})
 
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY)
+set(tools ${GCC_DIR})
+set(CMAKE_C_COMPILER \${tools}/bin/${TARGET}-gcc)
+set(CMAKE_CXX_COMPILER \${tools}/bin/${TARGET}-g++)
 
-  QEMU_ARGS+=(-L ${SYSROOT_FOLDER})
-  QEMU_ARGS+=(-E LD_LIBRARY_PATH=/lib)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+EOL
+CMAKE_ADDITIONAL_ARGS+=( -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" )
+QEMU_ARGS+=( -L "${SYSROOT_DIR}" )
+QEMU_ARGS+=( -E LD_LIBRARY_PATH=/lib )
 }
 
 function expand_codescape_config() {
-  assert_defined TARGET
-  local DATE=2017.10-08
-  local CODESCAPE_URL=https://codescape.mips.com/components/toolchain/${DATE}/Codescape.GNU.Tools.Package.${DATE}.for.MIPS.MTI.Linux.CentOS-5.x86_64.tar.gz
-  local GCC_URL=${CODESCAPE_URL}
-  local GCC_RELATIVE_FOLDER="mips-mti-linux-gnu/${DATE}"
-  unpackifnotexists "${GCC_URL}" "${GCC_RELATIVE_FOLDER}"
+  # ref: https://codescape.mips.com/components/toolchain/2020.06-01/downloads.html
+  # ref: https://codescape.mips.com/components/toolchain/2019.02-04/downloads.html
+  local -r DATE=2020.06-01
+  #local -r DATE=2019.02-04
+  local -r CODESCAPE_URL=https://codescape.mips.com/components/toolchain/${DATE}/Codescape.GNU.Tools.Package.${DATE}.for.MIPS.MTI.Linux.CentOS-6.x86_64.tar.gz
+  #local -r CODESCAPE_URL=https://codescape.mips.com/components/toolchain/${DATE}/Codescape.GNU.Tools.Package.${DATE}.for.MIPS.IMG.Linux.CentOS-6.x86_64.tar.gz
+  local -r GCC_URL=${CODESCAPE_URL}
+  local -r GCC_RELATIVE_DIR="mips-mti-linux-gnu/${DATE}"
+  #local -r GCC_RELATIVE_DIR="mips-img-linux-gnu/${DATE}"
+  unpack "${GCC_URL}" "${GCC_RELATIVE_DIR}"
 
-  local GCC_FOLDER=${ARCHIVE_FOLDER}/${GCC_RELATIVE_FOLDER}
+  local -r GCC_DIR=${ARCHIVE_DIR}/${GCC_RELATIVE_DIR}
   local MIPS_FLAGS=""
-  local LIBC_FOLDER_SUFFIX=""
+  local LIBC_DIR_SUFFIX=""
   local FLAVOUR=""
   case "${TARGET}" in
-    "mips32")    MIPS_FLAGS="-EB -mabi=32"; FLAVOUR="mips-r2-hard"; LIBC_FOLDER_SUFFIX="lib" ;;
-    "mips32el")  MIPS_FLAGS="-EL -mabi=32"; FLAVOUR="mipsel-r2-hard"; LIBC_FOLDER_SUFFIX="lib" ;;
-    "mips64")    MIPS_FLAGS="-EB -mabi=64"; FLAVOUR="mips-r2-hard"; LIBC_FOLDER_SUFFIX="lib64" ;;
-    "mips64el")  MIPS_FLAGS="-EL -mabi=64"; FLAVOUR="mipsel-r2-hard"; LIBC_FOLDER_SUFFIX="lib64" ;;
-    *)           echo 'unknown mips platform'; exit 1;;
+    "mips32")
+      MIPS_FLAGS="-EB -mips32r6 -mabi=32"
+      FLAVOUR="mips-r6-hard"
+      #MIPS_FLAGS="-EB -mips32r2 -mabi=32"
+      #FLAVOUR="mips-r2-hard"
+      LIBC_DIR_SUFFIX="lib"
+      ;;
+    "mips32el")
+      MIPS_FLAGS="-EL -mips32r6 -mabi=32"
+      FLAVOUR="mipsel-r6-hard"
+      #MIPS_FLAGS="-EL -mips32r2 -mabi=32"
+      #FLAVOUR="mipsel-r2-hard"
+      LIBC_DIR_SUFFIX="lib"
+      ;;
+    "mips64")
+      MIPS_FLAGS="-EB -mips64r6 -mabi=64"
+      FLAVOUR="mips-r6-hard"
+      #FLAVOUR="mips-r2-hard"
+      LIBC_DIR_SUFFIX="lib64"
+      ;;
+    "mips64el")
+      MIPS_FLAGS="-EL -mips64r6 -mabi=64"
+      FLAVOUR="mipsel-r6-hard"
+      #FLAVOUR="mipsel-r2-hard"
+      LIBC_DIR_SUFFIX="lib64"
+      ;;
+    *)
+      >&2 echo 'unknown mips platform'
+      exit 1 ;;
   esac
+  local -r SYSROOT_DIR=${GCC_DIR}/sysroot
+  local -r STAGING_DIR=${SYSROOT_DIR}-stage
 
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_FIND_ROOT_PATH=${GCC_FOLDER})
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSTEM_NAME=Linux)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSTEM_PROCESSOR=${TARGET})
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_C_COMPILER=mips-mti-linux-gnu-gcc)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_CXX_COMPILER=mips-mti-linux-gnu-g++)
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_C_COMPILER_ARG1="${MIPS_FLAGS}")
-  CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_CXX_COMPILER_ARG1="${MIPS_FLAGS}")
+  # Write a Toolchain file
+  # note: This is manadatory to use a file in order to have the CMake variable
+  # 'CMAKE_CROSSCOMPILING' set to TRUE.
+  # ref: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-linux
+  cat >"${TOOLCHAIN_FILE}" <<EOL
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR ${TARGET})
 
-  local SYSROOT_FOLDER=${GCC_FOLDER}/sysroot/${FLAVOUR}
+set(CMAKE_SYSROOT ${SYSROOT_DIR})
+set(CMAKE_STAGING_PREFIX ${STAGING_DIR})
 
-  # Keeping only the sysroot of interest to save on travis cache.
-  if [[ "${CONTINUOUS_INTEGRATION}" = "true" ]]; then
-    for folder in ${GCC_FOLDER}/sysroot/*; do
-      if [[ "${folder}" != "${SYSROOT_FOLDER}" ]]; then
-        rm -rf ${folder}
-      fi
-    done
-  fi
+set(tools ${GCC_DIR})
 
-  local LIBC_FOLDER=${GCC_FOLDER}/mips-mti-linux-gnu/lib/${FLAVOUR}/${LIBC_FOLDER_SUFFIX}
-  QEMU_ARGS+=(-L ${SYSROOT_FOLDER})
-  QEMU_ARGS+=(-E LD_PRELOAD=${LIBC_FOLDER}/libstdc++.so.6:${LIBC_FOLDER}/libgcc_s.so.1)
+set(CMAKE_C_COMPILER \${tools}/bin/mips-mti-linux-gnu-gcc)
+#set(CMAKE_C_COMPILER \${tools}/bin/mips-img-linux-gnu-gcc)
+set(CMAKE_C_FLAGS "${MIPS_FLAGS}")
+
+set(CMAKE_CXX_COMPILER \${tools}/bin/mips-mti-linux-gnu-g++)
+#set(CMAKE_CXX_COMPILER \${tools}/bin/mips-img-linux-gnu-g++)
+set(CMAKE_CXX_FLAGS "${MIPS_FLAGS}")
+
+set(CMAKE_FIND_ROOT_PATH ${GCC_DIR})
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+EOL
+
+CMAKE_ADDITIONAL_ARGS+=( -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}" )
+QEMU_ARGS+=( -L "${SYSROOT_DIR}/${FLAVOUR}" )
+local -r LIBC_DIR=${GCC_DIR}/mips-mti-linux-gnu/lib/${FLAVOUR}/${LIBC_DIR_SUFFIX}
+#local -r LIBC_DIR=${GCC_DIR}/mips-img-linux-gnu/lib/${FLAVOUR}/${LIBC_DIR_SUFFIX}
+QEMU_ARGS+=( -E LD_PRELOAD="${LIBC_DIR}/libstdc++.so.6:${LIBC_DIR}/libgcc_s.so.1" )
 }
 
-function expand_environment_and_integrate() {
-  assert_defined PROJECT_FOLDER
+function build() {
+  cd "${PROJECT_DIR}" || exit 2
+  set -x
+  clean_build
+  cmake -S. -B"${BUILD_DIR}" "${CMAKE_DEFAULT_ARGS[@]}" "${CMAKE_ADDITIONAL_ARGS[@]}"
+  cmake --build "${BUILD_DIR}" --target all -j8 -v
+  set +x
+}
+
+function run_test() {
+  assert_defined QEMU_ARCH
+  if [[ "${QEMU_ARCH}" == "DISABLED" ]]; then
+    >&2 echo "QEMU is disabled for ${TARGET}"
+    return
+  fi
+  install_qemu
+  RUN_CMD="${QEMU_INSTALL}/bin/qemu-${QEMU_ARCH} ${QEMU_ARGS[*]}"
+
+  cd "${BUILD_DIR}" || exit 2
+  set -x
+  for test_binary in "${BUILD_DIR}"/list_cpu_feature* ; do
+      ${RUN_CMD} "${test_binary}"
+  done
+  set +x
+}
+
+function usage() {
+  local -r NAME=$(basename "$0")
+  echo -e "$NAME - Build using a cross toolchain.
+
+SYNOPSIS
+\t$NAME [-h|--help] [toolchain|build|qemu|test|all]
+
+DESCRIPTION
+\tCross compile using a cross toolchain.
+
+\tYou MUST define the following variables before running this script:
+\t* TARGET:
+\t\tx86_64
+\t\taarch64-linux-gnu aarch64_be-linux-gnu
+\t\tarm-linux-gnueabihf armv8l-linux-gnueabihf arm-linux-gnueabi
+\t\tarmeb-linux-gnueabihf armeb-linux-gnueabi
+\t\tmips32 mips32el
+\t\tmips64 mips64el
+
+OPTIONS
+\t-h --help: show this help text
+\ttoolchain: download, unpack toolchain and generate CMake toolchain file
+\tbuild: toolchain + build the project using the toolchain file (note: remove previous build dir)
+\tqemu: download, unpack and build qemu
+\ttest: qemu + run all executable using qemu (note: don't build !)
+\tall: build + test (default)
+
+EXAMPLES
+* Using export:
+export TARGET=aarch64-linux-gnu
+$0
+
+* One-liner:
+TARGET=aarch64-linux-gnu $0"
+}
+
+# Main
+function main() {
+  case ${1} in
+    -h | --help)
+      usage; exit ;;
+  esac
+
   assert_defined TARGET
 
-  BUILD_DIR="${PROJECT_FOLDER}/cmake_build/${TARGET}"
-  mkdir -p "${BUILD_DIR}"
+  declare -r PROJECT_DIR="$(cd -P -- "$(dirname -- "$0")/.." && pwd -P)"
+  declare -r ARCHIVE_DIR="${PROJECT_DIR}/build_cross/archives"
+  declare -r BUILD_DIR="${PROJECT_DIR}/build_cross/${TARGET}"
+  declare -r TOOLCHAIN_FILE=${ARCHIVE_DIR}/toolchain_${TARGET}.cmake
 
-  declare -a CONFIG_NAMES=()
-  declare -a QEMU_ARGS=()
+  echo "Target: '${TARGET}'"
+
+  echo "Project dir: '${PROJECT_DIR}'"
+  echo "Archive dir: '${ARCHIVE_DIR}'"
+  echo "Build dir: '${BUILD_DIR}'"
+  echo "toolchain file: '${TOOLCHAIN_FILE}'"
+
+  declare -a CMAKE_DEFAULT_ARGS=( -G ${CMAKE_GENERATOR:-"Ninja"} )
   declare -a CMAKE_ADDITIONAL_ARGS=()
 
-  case ${TOOLCHAIN} in
-    LINARO)    expand_linaro_config     ;;
-    CODESCAPE) expand_codescape_config  ;;
-    NATIVE)    QEMU_ARCH=""             ;;
-    *)         echo "Unknown toolchain '${TOOLCHAIN}'..."; exit 1;;
+  declare -a QEMU_ARGS=()
+  case ${TARGET} in
+    x86_64)
+      declare -r QEMU_ARCH=x86_64 ;;
+    arm-linux-gnueabihf | armv8l-linux-gnueabihf | arm-linux-gnueabi)
+      expand_linaro_config
+      declare -r QEMU_ARCH=arm ;;
+    armeb-linux-gnueabihf | armeb-linux-gnueabi)
+      expand_linaro_config
+      declare -r QEMU_ARCH=DISABLED ;;
+    aarch64-linux-gnu)
+      expand_linaro_config
+      declare -r QEMU_ARCH=aarch64 ;;
+    aarch64_be-linux-gnu)
+      expand_linaro_config
+      declare -r QEMU_ARCH=DISABLED ;;
+    mips32)
+      expand_codescape_config
+      declare -r QEMU_ARCH=mips ;;
+    mips32el)
+      expand_codescape_config
+      declare -r QEMU_ARCH=mipsel ;;
+    mips64)
+      expand_codescape_config
+      declare -r QEMU_ARCH=mips64 ;;
+    mips64el)
+      expand_codescape_config
+      declare -r QEMU_ARCH=mips64el ;;
+    *)
+      >&2 echo "Unknown TARGET '${TARGET}'..."
+      exit 1 ;;
   esac
-  integrate
+  declare -r QEMU_INSTALL=${ARCHIVE_DIR}/qemu-${QEMU_ARCH}
+
+  case ${1} in
+    toolchain)
+      exit ;;
+    build)
+      build ;;
+    qemu)
+      install_qemu ;;
+    test)
+      run_test ;;
+    *)
+      build
+      run_test ;;
+  esac
 }
 
-if [ "${CONTINUOUS_INTEGRATION}" = "true" ]; then
-  QEMU_ARCHES=${QEMU_ARCH}
-  expand_environment_and_integrate
-fi
+main "${1:-all}"
diff --git a/scripts/test_integration.sh b/scripts/test_integration.sh
index d1c61b0..0947e36 100755
--- a/scripts/test_integration.sh
+++ b/scripts/test_integration.sh
@@ -1,84 +1,58 @@
 #!/usr/bin/env bash
 
-source "$(dirname -- "$0")"/run_integration.sh
-
 # Toolchains for little-endian, 64-bit ARMv8 for GNU/Linux systems
 function set_aarch64-linux-gnu() {
-  TOOLCHAIN=LINARO
-  TARGET=aarch64-linux-gnu
-  QEMU_ARCH=aarch64
+  export TARGET=aarch64-linux-gnu
 }
 
 # Toolchains for little-endian, hard-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
 function set_arm-linux-gnueabihf() {
-  TOOLCHAIN=LINARO
-  TARGET=arm-linux-gnueabihf
-  QEMU_ARCH=arm
+  export TARGET=arm-linux-gnueabihf
 }
 
 # Toolchains for little-endian, 32-bit ARMv8 for GNU/Linux systems
 function set_armv8l-linux-gnueabihf() {
-  TOOLCHAIN=LINARO
-  TARGET=armv8l-linux-gnueabihf
-  QEMU_ARCH=arm
+  export TARGET=armv8l-linux-gnueabihf
 }
 
 # Toolchains for little-endian, soft-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
 function set_arm-linux-gnueabi() {
-  TOOLCHAIN=LINARO
-  TARGET=arm-linux-gnueabi
-  QEMU_ARCH=arm
+  export TARGET=arm-linux-gnueabi
 }
 
 # Toolchains for big-endian, 64-bit ARMv8 for GNU/Linux systems
 function set_aarch64_be-linux-gnu() {
-  TOOLCHAIN=LINARO
-  TARGET=aarch64_be-linux-gnu
-  QEMU_ARCH=DISABLED
+  export TARGET=aarch64_be-linux-gnu
 }
 
 # Toolchains for big-endian, hard-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
 function set_armeb-linux-gnueabihf() {
-  TOOLCHAIN=LINARO
-  TARGET=armeb-linux-gnueabihf
-  QEMU_ARCH=DISABLED
+  export TARGET=armeb-linux-gnueabihf
 }
 
 # Toolchains for big-endian, soft-float, 32-bit ARMv7 (and earlier) for GNU/Linux systems
 function set_armeb-linux-gnueabi() {
-  TOOLCHAIN=LINARO
-  TARGET=armeb-linux-gnueabi
-  QEMU_ARCH=DISABLED
+  export TARGET=armeb-linux-gnueabi
 }
 
 function set_mips32() {
-  TOOLCHAIN=CODESCAPE
-  TARGET=mips32
-  QEMU_ARCH=mips
+  export TARGET=mips32
 }
 
 function set_mips32el() {
-  TOOLCHAIN=CODESCAPE
-  TARGET=mips32el
-  QEMU_ARCH=mipsel
+  export TARGET=mips32el
 }
 
 function set_mips64() {
-  TOOLCHAIN=CODESCAPE
-  TARGET=mips64
-  QEMU_ARCH=mips64
+  export TARGET=mips64
 }
 
 function set_mips64el() {
-  TOOLCHAIN=CODESCAPE
-  TARGET=mips64el
-  QEMU_ARCH=mips64el
+  export TARGET=mips64el
 }
 
-function set_native() {
-  TOOLCHAIN=NATIVE
-  TARGET=native
-  QEMU_ARCH=""
+function set_x86_64() {
+  export TARGET=x86_64
 }
 
 ENVIRONMENTS="
@@ -93,14 +67,13 @@
   set_mips32el
   set_mips64
   set_mips64el
-  set_native
+  set_x86_64
 "
 
 set -e
 
-CMAKE_GENERATOR="Ninja"
-
 for SET_ENVIRONMENT in ${ENVIRONMENTS}; do
+  echo "testing ${SET_ENVIRONMENT}"
   ${SET_ENVIRONMENT}
-  expand_environment_and_integrate
+  ./"$(dirname -- "$0")"/run_integration.sh
 done
diff --git a/src/copy.inl b/src/copy.inl
new file mode 100644
index 0000000..47771d4
--- /dev/null
+++ b/src/copy.inl
@@ -0,0 +1,19 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stddef.h>
+
+static void copy(char *__restrict dst, const char *src, size_t count) {
+  for (size_t i = 0; i < count; ++i) dst[i] = src[i];
+}
diff --git a/src/cpuinfo_aarch64.c b/src/cpuinfo_aarch64.c
deleted file mode 100644
index 0a52718..0000000
--- a/src/cpuinfo_aarch64.c
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2017 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cpuinfo_aarch64.h"
-
-#include <assert.h>
-#include <ctype.h>
-
-#include "internal/filesystem.h"
-#include "internal/hwcaps.h"
-#include "internal/stack_line_reader.h"
-#include "internal/string_view.h"
-
-// Generation of feature's getters/setters functions and kGetters, kSetters,
-// kCpuInfoFlags and kHardwareCapabilities global tables.
-#define DEFINE_TABLE_FEATURES                                                 \
-  FEATURE(AARCH64_FP, fp, "fp", AARCH64_HWCAP_FP, 0)                          \
-  FEATURE(AARCH64_ASIMD, asimd, "asimd", AARCH64_HWCAP_ASIMD, 0)              \
-  FEATURE(AARCH64_EVTSTRM, evtstrm, "evtstrm", AARCH64_HWCAP_EVTSTRM, 0)      \
-  FEATURE(AARCH64_AES, aes, "aes", AARCH64_HWCAP_AES, 0)                      \
-  FEATURE(AARCH64_PMULL, pmull, "pmull", AARCH64_HWCAP_PMULL, 0)              \
-  FEATURE(AARCH64_SHA1, sha1, "sha1", AARCH64_HWCAP_SHA1, 0)                  \
-  FEATURE(AARCH64_SHA2, sha2, "sha2", AARCH64_HWCAP_SHA2, 0)                  \
-  FEATURE(AARCH64_CRC32, crc32, "crc32", AARCH64_HWCAP_CRC32, 0)              \
-  FEATURE(AARCH64_ATOMICS, atomics, "atomics", AARCH64_HWCAP_ATOMICS, 0)      \
-  FEATURE(AARCH64_FPHP, fphp, "fphp", AARCH64_HWCAP_FPHP, 0)                  \
-  FEATURE(AARCH64_ASIMDHP, asimdhp, "asimdhp", AARCH64_HWCAP_ASIMDHP, 0)      \
-  FEATURE(AARCH64_CPUID, cpuid, "cpuid", AARCH64_HWCAP_CPUID, 0)              \
-  FEATURE(AARCH64_ASIMDRDM, asimdrdm, "asimdrdm", AARCH64_HWCAP_ASIMDRDM, 0)  \
-  FEATURE(AARCH64_JSCVT, jscvt, "jscvt", AARCH64_HWCAP_JSCVT, 0)              \
-  FEATURE(AARCH64_FCMA, fcma, "fcma", AARCH64_HWCAP_FCMA, 0)                  \
-  FEATURE(AARCH64_LRCPC, lrcpc, "lrcpc", AARCH64_HWCAP_LRCPC, 0)              \
-  FEATURE(AARCH64_DCPOP, dcpop, "dcpop", AARCH64_HWCAP_DCPOP, 0)              \
-  FEATURE(AARCH64_SHA3, sha3, "sha3", AARCH64_HWCAP_SHA3, 0)                  \
-  FEATURE(AARCH64_SM3, sm3, "sm3", AARCH64_HWCAP_SM3, 0)                      \
-  FEATURE(AARCH64_SM4, sm4, "sm4", AARCH64_HWCAP_SM4, 0)                      \
-  FEATURE(AARCH64_ASIMDDP, asimddp, "asimddp", AARCH64_HWCAP_ASIMDDP, 0)      \
-  FEATURE(AARCH64_SHA512, sha512, "sha512", AARCH64_HWCAP_SHA512, 0)          \
-  FEATURE(AARCH64_SVE, sve, "sve", AARCH64_HWCAP_SVE, 0)                      \
-  FEATURE(AARCH64_ASIMDFHM, asimdfhm, "asimdfhm", AARCH64_HWCAP_ASIMDFHM, 0)  \
-  FEATURE(AARCH64_DIT, dit, "dit", AARCH64_HWCAP_DIT, 0)                      \
-  FEATURE(AARCH64_USCAT, uscat, "uscat", AARCH64_HWCAP_USCAT, 0)              \
-  FEATURE(AARCH64_ILRCPC, ilrcpc, "ilrcpc", AARCH64_HWCAP_ILRCPC, 0)          \
-  FEATURE(AARCH64_FLAGM, flagm, "flagm", AARCH64_HWCAP_FLAGM, 0)              \
-  FEATURE(AARCH64_SSBS, ssbs, "ssbs", AARCH64_HWCAP_SSBS, 0)                  \
-  FEATURE(AARCH64_SB, sb, "sb", AARCH64_HWCAP_SB, 0)                          \
-  FEATURE(AARCH64_PACA, paca, "paca", AARCH64_HWCAP_PACA, 0)                  \
-  FEATURE(AARCH64_PACG, pacg, "pacg", AARCH64_HWCAP_PACG, 0)                  \
-  FEATURE(AARCH64_DCPODP, dcpodp, "dcpodp", 0, AARCH64_HWCAP2_DCPODP)         \
-  FEATURE(AARCH64_SVE2, sve2, "sve2", 0, AARCH64_HWCAP2_SVE2)                 \
-  FEATURE(AARCH64_SVEAES, sveaes, "sveaes", 0, AARCH64_HWCAP2_SVEAES)         \
-  FEATURE(AARCH64_SVEPMULL, svepmull, "svepmull", 0, AARCH64_HWCAP2_SVEPMULL) \
-  FEATURE(AARCH64_SVEBITPERM, svebitperm, "svebitperm", 0,                    \
-          AARCH64_HWCAP2_SVEBITPERM)                                          \
-  FEATURE(AARCH64_SVESHA3, svesha3, "svesha3", 0, AARCH64_HWCAP2_SVESHA3)     \
-  FEATURE(AARCH64_SVESM4, svesm4, "svesm4", 0, AARCH64_HWCAP2_SVESM4)         \
-  FEATURE(AARCH64_FLAGM2, flagm2, "flagm2", 0, AARCH64_HWCAP2_FLAGM2)         \
-  FEATURE(AARCH64_FRINT, frint, "frint", 0, AARCH64_HWCAP2_FRINT)             \
-  FEATURE(AARCH64_SVEI8MM, svei8mm, "svei8mm", 0, AARCH64_HWCAP2_SVEI8MM)     \
-  FEATURE(AARCH64_SVEF32MM, svef32mm, "svef32mm", 0, AARCH64_HWCAP2_SVEF32MM) \
-  FEATURE(AARCH64_SVEF64MM, svef64mm, "svef64mm", 0, AARCH64_HWCAP2_SVEF64MM) \
-  FEATURE(AARCH64_SVEBF16, svebf16, "svebf16", 0, AARCH64_HWCAP2_SVEBF16)     \
-  FEATURE(AARCH64_I8MM, i8mm, "i8mm", 0, AARCH64_HWCAP2_I8MM)                 \
-  FEATURE(AARCH64_BF16, bf16, "bf16", 0, AARCH64_HWCAP2_BF16)                 \
-  FEATURE(AARCH64_DGH, dgh, "dgh", 0, AARCH64_HWCAP2_DGH)                     \
-  FEATURE(AARCH64_RNG, rng, "rng", 0, AARCH64_HWCAP2_RNG)                     \
-  FEATURE(AARCH64_BTI, bti, "bti", 0, AARCH64_HWCAP2_BTI)
-#define DEFINE_TABLE_FEATURE_TYPE Aarch64Features
-#include "define_tables.h"
-
-static bool HandleAarch64Line(const LineResult result,
-                              Aarch64Info* const info) {
-  StringView line = result.line;
-  StringView key, value;
-  if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
-    if (CpuFeatures_StringView_IsEquals(key, str("Features"))) {
-      for (size_t i = 0; i < AARCH64_LAST_; ++i) {
-        kSetters[i](&info->features,
-                    CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
-      }
-    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer"))) {
-      info->implementer = CpuFeatures_StringView_ParsePositiveNumber(value);
-    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU variant"))) {
-      info->variant = CpuFeatures_StringView_ParsePositiveNumber(value);
-    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU part"))) {
-      info->part = CpuFeatures_StringView_ParsePositiveNumber(value);
-    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU revision"))) {
-      info->revision = CpuFeatures_StringView_ParsePositiveNumber(value);
-    }
-  }
-  return !result.eof;
-}
-
-static void FillProcCpuInfoData(Aarch64Info* const info) {
-  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
-  if (fd >= 0) {
-    StackLineReader reader;
-    StackLineReader_Initialize(&reader, fd);
-    for (;;) {
-      if (!HandleAarch64Line(StackLineReader_NextLine(&reader), info)) {
-        break;
-      }
-    }
-    CpuFeatures_CloseFile(fd);
-  }
-}
-
-static const Aarch64Info kEmptyAarch64Info;
-
-Aarch64Info GetAarch64Info(void) {
-  // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
-  // have some information if the executable is sandboxed (aka no access to
-  // /proc/cpuinfo).
-  Aarch64Info info = kEmptyAarch64Info;
-
-  FillProcCpuInfoData(&info);
-  const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
-  for (size_t i = 0; i < AARCH64_LAST_; ++i) {
-    if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
-      kSetters[i](&info.features, true);
-    }
-  }
-
-  return info;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Introspection functions
-
-int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
-                                Aarch64FeaturesEnum value) {
-  if (value >= AARCH64_LAST_) return false;
-  return kGetters[value](features);
-}
-
-const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum value) {
-  if (value >= AARCH64_LAST_) return "unknown feature";
-  return kCpuInfoFlags[value];
-}
diff --git a/src/cpuinfo_ppc.c b/src/cpuinfo_ppc.c
deleted file mode 100644
index 24401f9..0000000
--- a/src/cpuinfo_ppc.c
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2018 IBM.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cpuinfo_ppc.h"
-
-#include <assert.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "internal/bit_utils.h"
-#include "internal/filesystem.h"
-#include "internal/stack_line_reader.h"
-#include "internal/string_view.h"
-
-// Generation of feature's getters/setters functions and kGetters, kSetters,
-// kCpuInfoFlags and kHardwareCapabilities global tables.
-#define DEFINE_TABLE_FEATURES                                                  \
-  FEATURE(PPC_32, ppc32, "ppc32", PPC_FEATURE_32, 0)                           \
-  FEATURE(PPC_64, ppc64, "ppc64", PPC_FEATURE_64, 0)                           \
-  FEATURE(PPC_601_INSTR, ppc601, "ppc601", PPC_FEATURE_601_INSTR, 0)           \
-  FEATURE(PPC_HAS_ALTIVEC, altivec, "altivec", PPC_FEATURE_HAS_ALTIVEC, 0)     \
-  FEATURE(PPC_HAS_FPU, fpu, "fpu", PPC_FEATURE_HAS_FPU, 0)                     \
-  FEATURE(PPC_HAS_MMU, mmu, "mmu", PPC_FEATURE_HAS_MMU, 0)                     \
-  FEATURE(PPC_HAS_4xxMAC, mac_4xx, "4xxmac", PPC_FEATURE_HAS_4xxMAC, 0)        \
-  FEATURE(PPC_UNIFIED_CACHE, unifiedcache, "ucache",                           \
-          PPC_FEATURE_UNIFIED_CACHE, 0)                                        \
-  FEATURE(PPC_HAS_SPE, spe, "spe", PPC_FEATURE_HAS_SPE, 0)                     \
-  FEATURE(PPC_HAS_EFP_SINGLE, efpsingle, "efpsingle",                          \
-          PPC_FEATURE_HAS_EFP_SINGLE, 0)                                       \
-  FEATURE(PPC_HAS_EFP_DOUBLE, efpdouble, "efpdouble",                          \
-          PPC_FEATURE_HAS_EFP_DOUBLE, 0)                                       \
-  FEATURE(PPC_NO_TB, no_tb, "notb", PPC_FEATURE_NO_TB, 0)                      \
-  FEATURE(PPC_POWER4, power4, "power4", PPC_FEATURE_POWER4, 0)                 \
-  FEATURE(PPC_POWER5, power5, "power5", PPC_FEATURE_POWER5, 0)                 \
-  FEATURE(PPC_POWER5_PLUS, power5plus, "power5+", PPC_FEATURE_POWER5_PLUS, 0)  \
-  FEATURE(PPC_CELL, cell, "cellbe", PPC_FEATURE_CELL, 0)                       \
-  FEATURE(PPC_BOOKE, booke, "booke", PPC_FEATURE_BOOKE, 0)                     \
-  FEATURE(PPC_SMT, smt, "smt", PPC_FEATURE_SMT, 0)                             \
-  FEATURE(PPC_ICACHE_SNOOP, icachesnoop, "ic_snoop", PPC_FEATURE_ICACHE_SNOOP, \
-          0)                                                                   \
-  FEATURE(PPC_ARCH_2_05, arch205, "arch_2_05", PPC_FEATURE_ARCH_2_05, 0)       \
-  FEATURE(PPC_PA6T, pa6t, "pa6t", PPC_FEATURE_PA6T, 0)                         \
-  FEATURE(PPC_HAS_DFP, dfp, "dfp", PPC_FEATURE_HAS_DFP, 0)                     \
-  FEATURE(PPC_POWER6_EXT, power6ext, "power6x", PPC_FEATURE_POWER6_EXT, 0)     \
-  FEATURE(PPC_ARCH_2_06, arch206, "arch_2_06", PPC_FEATURE_ARCH_2_06, 0)       \
-  FEATURE(PPC_HAS_VSX, vsx, "vsx", PPC_FEATURE_HAS_VSX, 0)                     \
-  FEATURE(PPC_PSERIES_PERFMON_COMPAT, pseries_perfmon_compat, "archpmu",       \
-          PPC_FEATURE_PSERIES_PERFMON_COMPAT, 0)                               \
-  FEATURE(PPC_TRUE_LE, truele, "true_le", PPC_FEATURE_TRUE_LE, 0)              \
-  FEATURE(PPC_PPC_LE, ppcle, "ppcle", PPC_FEATURE_PPC_LE, 0)                   \
-  FEATURE(PPC_ARCH_2_07, arch207, "arch_2_07", 0, PPC_FEATURE2_ARCH_2_07)      \
-  FEATURE(PPC_HTM, htm, "htm", 0, PPC_FEATURE2_HTM)                            \
-  FEATURE(PPC_DSCR, dscr, "dscr", 0, PPC_FEATURE2_DSCR)                        \
-  FEATURE(PPC_EBB, ebb, "ebb", 0, PPC_FEATURE2_EBB)                            \
-  FEATURE(PPC_ISEL, isel, "isel", 0, PPC_FEATURE2_ISEL)                        \
-  FEATURE(PPC_TAR, tar, "tar", 0, PPC_FEATURE2_TAR)                            \
-  FEATURE(PPC_VEC_CRYPTO, vcrypto, "vcrypto", 0, PPC_FEATURE2_VEC_CRYPTO)      \
-  FEATURE(PPC_HTM_NOSC, htm_nosc, "htm-nosc", 0, PPC_FEATURE2_HTM_NOSC)        \
-  FEATURE(PPC_ARCH_3_00, arch300, "arch_3_00", 0, PPC_FEATURE2_ARCH_3_00)      \
-  FEATURE(PPC_HAS_IEEE128, ieee128, "ieee128", 0, PPC_FEATURE2_HAS_IEEE128)    \
-  FEATURE(PPC_DARN, darn, "darn", 0, PPC_FEATURE2_DARN)                        \
-  FEATURE(PPC_SCV, scv, "scv", 0, PPC_FEATURE2_SCV)                            \
-  FEATURE(PPC_HTM_NO_SUSPEND, htm_no_suspend, "htm-no-suspend", 0,             \
-          PPC_FEATURE2_HTM_NO_SUSPEND)
-#define DEFINE_TABLE_FEATURE_TYPE PPCFeatures
-#include "define_tables.h"
-
-static bool HandlePPCLine(const LineResult result,
-                          PPCPlatformStrings* const strings) {
-  StringView line = result.line;
-  StringView key, value;
-  if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
-    if (CpuFeatures_StringView_HasWord(key, "platform")) {
-      CpuFeatures_StringView_CopyString(value, strings->platform,
-                                        sizeof(strings->platform));
-    } else if (CpuFeatures_StringView_IsEquals(key, str("model"))) {
-      CpuFeatures_StringView_CopyString(value, strings->model,
-                                        sizeof(strings->platform));
-    } else if (CpuFeatures_StringView_IsEquals(key, str("machine"))) {
-      CpuFeatures_StringView_CopyString(value, strings->machine,
-                                        sizeof(strings->platform));
-    } else if (CpuFeatures_StringView_IsEquals(key, str("cpu"))) {
-      CpuFeatures_StringView_CopyString(value, strings->cpu,
-                                        sizeof(strings->platform));
-    }
-  }
-  return !result.eof;
-}
-
-static void FillProcCpuInfoData(PPCPlatformStrings* const strings) {
-  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
-  if (fd >= 0) {
-    StackLineReader reader;
-    StackLineReader_Initialize(&reader, fd);
-    for (;;) {
-      if (!HandlePPCLine(StackLineReader_NextLine(&reader), strings)) {
-        break;
-      }
-    }
-    CpuFeatures_CloseFile(fd);
-  }
-}
-
-static const PPCInfo kEmptyPPCInfo;
-
-PPCInfo GetPPCInfo(void) {
-  /*
-   * On Power feature flags aren't currently in cpuinfo so we only look at
-   * the auxilary vector.
-   */
-  PPCInfo info = kEmptyPPCInfo;
-  const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
-  for (size_t i = 0; i < PPC_LAST_; ++i) {
-    if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
-      kSetters[i](&info.features, true);
-    }
-  }
-  return info;
-}
-
-static const PPCPlatformStrings kEmptyPPCPlatformStrings;
-
-PPCPlatformStrings GetPPCPlatformStrings(void) {
-  PPCPlatformStrings strings = kEmptyPPCPlatformStrings;
-
-  FillProcCpuInfoData(&strings);
-  strings.type = CpuFeatures_GetPlatformType();
-  return strings;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Introspection functions
-
-int GetPPCFeaturesEnumValue(const PPCFeatures* features,
-                            PPCFeaturesEnum value) {
-  if (value >= PPC_LAST_) return false;
-  return kGetters[value](features);
-}
-
-const char* GetPPCFeaturesEnumName(PPCFeaturesEnum value) {
-  if (value >= PPC_LAST_) return "unknown feature";
-  return kCpuInfoFlags[value];
-}
diff --git a/src/define_introspection.inl b/src/define_introspection.inl
new file mode 100644
index 0000000..c0eb916
--- /dev/null
+++ b/src/define_introspection.inl
@@ -0,0 +1,86 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INTROSPECTION_PREFIX
+#error "missing INTROSPECTION_PREFIX"
+#endif
+#ifndef INTROSPECTION_ENUM_PREFIX
+#error "missing INTROSPECTION_ENUM_PREFIX"
+#endif
+#ifndef INTROSPECTION_TABLE
+#error "missing INTROSPECTION_TABLE"
+#endif
+
+#include <stdbool.h>
+
+#define STRINGIZE_(s) #s
+#define STRINGIZE(s) STRINGIZE_(s)
+
+#define FEAT_TYPE_NAME__(X) X##Features
+#define FEAT_TYPE_NAME_(X) FEAT_TYPE_NAME__(X)
+#define FEAT_TYPE_NAME FEAT_TYPE_NAME_(INTROSPECTION_PREFIX)
+
+#define FEAT_ENUM_NAME__(X) X##FeaturesEnum
+#define FEAT_ENUM_NAME_(X) FEAT_ENUM_NAME__(X)
+#define FEAT_ENUM_NAME FEAT_ENUM_NAME_(INTROSPECTION_PREFIX)
+
+#define GET_FEAT_ENUM_VALUE__(X) Get##X##FeaturesEnumValue
+#define GET_FEAT_ENUM_VALUE_(X) GET_FEAT_ENUM_VALUE__(X)
+#define GET_FEAT_ENUM_VALUE GET_FEAT_ENUM_VALUE_(INTROSPECTION_PREFIX)
+
+#define GET_FEAT_ENUM_NAME__(X) Get##X##FeaturesEnumName
+#define GET_FEAT_ENUM_NAME_(X) GET_FEAT_ENUM_NAME__(X)
+#define GET_FEAT_ENUM_NAME GET_FEAT_ENUM_NAME_(INTROSPECTION_PREFIX)
+
+#define FEAT_ENUM_LAST__(X) X##_LAST_
+#define FEAT_ENUM_LAST_(X) FEAT_ENUM_LAST__(X)
+#define FEAT_ENUM_LAST FEAT_ENUM_LAST_(INTROSPECTION_ENUM_PREFIX)
+
+// Generate individual getters and setters.
+#define LINE(ENUM, NAME, A, B, C)                                \
+  static void set_##ENUM(FEAT_TYPE_NAME* features, bool value) { \
+    features->NAME = value;                                      \
+  }                                                              \
+  static int get_##ENUM(const FEAT_TYPE_NAME* features) {        \
+    return features->NAME;                                       \
+  }
+INTROSPECTION_TABLE
+#undef LINE
+
+// Generate getters table
+#define LINE(ENUM, NAME, A, B, C) [ENUM] = get_##ENUM,
+static int (*const kGetters[])(const FEAT_TYPE_NAME*) = {INTROSPECTION_TABLE};
+#undef LINE
+
+// Generate setters table
+#define LINE(ENUM, NAME, A, B, C) [ENUM] = set_##ENUM,
+static void (*const kSetters[])(FEAT_TYPE_NAME*, bool) = {INTROSPECTION_TABLE};
+#undef LINE
+
+// Implements the `GetXXXFeaturesEnumValue` API.
+int GET_FEAT_ENUM_VALUE(const FEAT_TYPE_NAME* features, FEAT_ENUM_NAME value) {
+  if (value >= FEAT_ENUM_LAST) return false;
+  return kGetters[value](features);
+}
+
+// Generate feature name table.
+#define LINE(ENUM, NAME, A, B, C) [ENUM] = STRINGIZE(NAME),
+static const char* kFeatureNames[] = {INTROSPECTION_TABLE};
+#undef LINE
+
+// Implements the `GetXXXFeaturesEnumName` API.
+const char* GET_FEAT_ENUM_NAME(FEAT_ENUM_NAME value) {
+  if (value >= FEAT_ENUM_LAST) return "unknown_feature";
+  return kFeatureNames[value];
+}
diff --git a/src/define_introspection_and_hwcaps.inl b/src/define_introspection_and_hwcaps.inl
new file mode 100644
index 0000000..c31b60d
--- /dev/null
+++ b/src/define_introspection_and_hwcaps.inl
@@ -0,0 +1,26 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "define_introspection.inl"
+#include "internal/hwcaps.h"
+
+#define LINE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) \
+  [ENUM] = (HardwareCapabilities){HWCAP, HWCAP2},
+static const HardwareCapabilities kHardwareCapabilities[] = {
+    INTROSPECTION_TABLE};
+#undef LINE
+
+#define LINE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) [ENUM] = CPUINFO_FLAG,
+static const char* kCpuInfoFlags[] = {INTROSPECTION_TABLE};
+#undef LINE
diff --git a/src/define_tables.h b/src/define_tables.h
deleted file mode 100644
index dc1485c..0000000
--- a/src/define_tables.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2020 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// The following preprocessor constants must be defined before including this
-// file:
-//  - DEFINE_TABLE_FEATURE_TYPE, the underlying type (e.g. X86Features)
-//  - DEFINE_TABLE_FEATURES, the list of FEATURE macros to be inserted.
-
-// This file is to be included once per `cpuinfo_XXX.c` in order to construct
-// feature getters and setters functions as well as several enum indexed tables
-// from the db file.
-// - `kGetters` a table of getters function pointers from feature enum to
-// retrieve a feature,
-// - `kSetters` a table of setters function pointers from feature enum to set a
-// feature,
-// - `kCpuInfoFlags` a table of strings from feature enum to /proc/cpuinfo
-// flags,
-// - `kHardwareCapabilities` a table of HardwareCapabilities structs indexed by
-// their feature enum.
-
-#ifndef SRC_DEFINE_TABLES_H_
-#define SRC_DEFINE_TABLES_H_
-
-#define FEATURE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) [ENUM] = CPUINFO_FLAG,
-static const char* kCpuInfoFlags[] = {DEFINE_TABLE_FEATURES};
-#undef FEATURE
-
-#ifndef DEFINE_TABLE_DONT_GENERATE_HWCAPS
-#define FEATURE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) \
-  [ENUM] = (HardwareCapabilities){HWCAP, HWCAP2},
-static const HardwareCapabilities kHardwareCapabilities[] = {
-    DEFINE_TABLE_FEATURES};
-#undef FEATURE
-#endif  // DEFINE_TABLE_DONT_GENERATE_HWCAPS
-
-#define FEATURE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2)                    \
-  static void set_##ENUM(DEFINE_TABLE_FEATURE_TYPE* features, bool value) { \
-    features->NAME = value;                                                 \
-  }                                                                         \
-  static int get_##ENUM(const DEFINE_TABLE_FEATURE_TYPE* features) {        \
-    return features->NAME;                                                  \
-  }
-DEFINE_TABLE_FEATURES
-#undef FEATURE
-
-#define FEATURE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) [ENUM] = set_##ENUM,
-static void (*const kSetters[])(DEFINE_TABLE_FEATURE_TYPE*,
-                                bool) = {DEFINE_TABLE_FEATURES};
-#undef FEATURE
-
-#define FEATURE(ENUM, NAME, CPUINFO_FLAG, HWCAP, HWCAP2) [ENUM] = get_##ENUM,
-static int (*const kGetters[])(const DEFINE_TABLE_FEATURE_TYPE*) = {
-    DEFINE_TABLE_FEATURES};
-#undef FEATURE
-
-#endif  // SRC_DEFINE_TABLES_H_
diff --git a/src/equals.inl b/src/equals.inl
new file mode 100644
index 0000000..67a115f
--- /dev/null
+++ b/src/equals.inl
@@ -0,0 +1,22 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdbool.h>
+#include <stddef.h>
+
+static bool equals(const char *lhs, const char *rhs, size_t count) {
+  for (size_t i = 0; i < count; ++i)
+    if (lhs[i] != rhs[i]) return false;
+  return true;
+}
diff --git a/src/hwcaps.c b/src/hwcaps.c
index dd17e3b..f44f6c3 100644
--- a/src/hwcaps.c
+++ b/src/hwcaps.c
@@ -35,7 +35,8 @@
 #ifdef CPU_FEATURES_TEST
 // In test mode, hwcaps_for_testing will define the following functions.
 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
-PlatformType CpuFeatures_GetPlatformType(void);
+const char* CpuFeatures_GetPlatformPointer(void);
+const char* CpuFeatures_GetBasePlatformPointer(void);
 #else
 
 // Debug facilities
@@ -67,13 +68,7 @@
 #elif defined(HAVE_DLFCN_H)
 // On Android we probe the system's C library for a 'getauxval' function and
 // call it if it exits, or return 0 for failure. This function is available
-// since API level 20.
-//
-// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the edge
-// case where some NDK developers use headers for a platform that is newer than
-// the one really targetted by their application. This is typically done to use
-// newer native APIs only when running on more recent Android versions, and
-// requires careful symbol management.
+// since API level 18.
 //
 // Note that getauxval() can't really be re-implemented here, because its
 // implementation does not parse /proc/self/auxv. Instead it depends on values
@@ -163,20 +158,12 @@
   return capabilities;
 }
 
-PlatformType kEmptyPlatformType;
+const char *CpuFeatures_GetPlatformPointer(void) {
+  return (const char *)GetHardwareCapabilitiesFor(AT_PLATFORM);
+}
 
-PlatformType CpuFeatures_GetPlatformType(void) {
-  PlatformType type = kEmptyPlatformType;
-  char *platform = (char *)GetHardwareCapabilitiesFor(AT_PLATFORM);
-  char *base_platform = (char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM);
-
-  if (platform != NULL)
-    CpuFeatures_StringView_CopyString(str(platform), type.platform,
-                                      sizeof(type.platform));
-  if (base_platform != NULL)
-    CpuFeatures_StringView_CopyString(str(base_platform), type.base_platform,
-                                      sizeof(type.base_platform));
-  return type;
+const char *CpuFeatures_GetBasePlatformPointer(void) {
+  return (const char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM);
 }
 
 #endif  // CPU_FEATURES_TEST
diff --git a/src/impl_aarch64_linux_or_android.c b/src/impl_aarch64_linux_or_android.c
new file mode 100644
index 0000000..745beb9
--- /dev/null
+++ b/src/impl_aarch64_linux_or_android.c
@@ -0,0 +1,150 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_AARCH64
+#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+
+#include "cpuinfo_aarch64.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                                                \
+  LINE(AARCH64_FP, fp, "fp", AARCH64_HWCAP_FP, 0)                          \
+  LINE(AARCH64_ASIMD, asimd, "asimd", AARCH64_HWCAP_ASIMD, 0)              \
+  LINE(AARCH64_EVTSTRM, evtstrm, "evtstrm", AARCH64_HWCAP_EVTSTRM, 0)      \
+  LINE(AARCH64_AES, aes, "aes", AARCH64_HWCAP_AES, 0)                      \
+  LINE(AARCH64_PMULL, pmull, "pmull", AARCH64_HWCAP_PMULL, 0)              \
+  LINE(AARCH64_SHA1, sha1, "sha1", AARCH64_HWCAP_SHA1, 0)                  \
+  LINE(AARCH64_SHA2, sha2, "sha2", AARCH64_HWCAP_SHA2, 0)                  \
+  LINE(AARCH64_CRC32, crc32, "crc32", AARCH64_HWCAP_CRC32, 0)              \
+  LINE(AARCH64_ATOMICS, atomics, "atomics", AARCH64_HWCAP_ATOMICS, 0)      \
+  LINE(AARCH64_FPHP, fphp, "fphp", AARCH64_HWCAP_FPHP, 0)                  \
+  LINE(AARCH64_ASIMDHP, asimdhp, "asimdhp", AARCH64_HWCAP_ASIMDHP, 0)      \
+  LINE(AARCH64_CPUID, cpuid, "cpuid", AARCH64_HWCAP_CPUID, 0)              \
+  LINE(AARCH64_ASIMDRDM, asimdrdm, "asimdrdm", AARCH64_HWCAP_ASIMDRDM, 0)  \
+  LINE(AARCH64_JSCVT, jscvt, "jscvt", AARCH64_HWCAP_JSCVT, 0)              \
+  LINE(AARCH64_FCMA, fcma, "fcma", AARCH64_HWCAP_FCMA, 0)                  \
+  LINE(AARCH64_LRCPC, lrcpc, "lrcpc", AARCH64_HWCAP_LRCPC, 0)              \
+  LINE(AARCH64_DCPOP, dcpop, "dcpop", AARCH64_HWCAP_DCPOP, 0)              \
+  LINE(AARCH64_SHA3, sha3, "sha3", AARCH64_HWCAP_SHA3, 0)                  \
+  LINE(AARCH64_SM3, sm3, "sm3", AARCH64_HWCAP_SM3, 0)                      \
+  LINE(AARCH64_SM4, sm4, "sm4", AARCH64_HWCAP_SM4, 0)                      \
+  LINE(AARCH64_ASIMDDP, asimddp, "asimddp", AARCH64_HWCAP_ASIMDDP, 0)      \
+  LINE(AARCH64_SHA512, sha512, "sha512", AARCH64_HWCAP_SHA512, 0)          \
+  LINE(AARCH64_SVE, sve, "sve", AARCH64_HWCAP_SVE, 0)                      \
+  LINE(AARCH64_ASIMDFHM, asimdfhm, "asimdfhm", AARCH64_HWCAP_ASIMDFHM, 0)  \
+  LINE(AARCH64_DIT, dit, "dit", AARCH64_HWCAP_DIT, 0)                      \
+  LINE(AARCH64_USCAT, uscat, "uscat", AARCH64_HWCAP_USCAT, 0)              \
+  LINE(AARCH64_ILRCPC, ilrcpc, "ilrcpc", AARCH64_HWCAP_ILRCPC, 0)          \
+  LINE(AARCH64_FLAGM, flagm, "flagm", AARCH64_HWCAP_FLAGM, 0)              \
+  LINE(AARCH64_SSBS, ssbs, "ssbs", AARCH64_HWCAP_SSBS, 0)                  \
+  LINE(AARCH64_SB, sb, "sb", AARCH64_HWCAP_SB, 0)                          \
+  LINE(AARCH64_PACA, paca, "paca", AARCH64_HWCAP_PACA, 0)                  \
+  LINE(AARCH64_PACG, pacg, "pacg", AARCH64_HWCAP_PACG, 0)                  \
+  LINE(AARCH64_DCPODP, dcpodp, "dcpodp", 0, AARCH64_HWCAP2_DCPODP)         \
+  LINE(AARCH64_SVE2, sve2, "sve2", 0, AARCH64_HWCAP2_SVE2)                 \
+  LINE(AARCH64_SVEAES, sveaes, "sveaes", 0, AARCH64_HWCAP2_SVEAES)         \
+  LINE(AARCH64_SVEPMULL, svepmull, "svepmull", 0, AARCH64_HWCAP2_SVEPMULL) \
+  LINE(AARCH64_SVEBITPERM, svebitperm, "svebitperm", 0,                    \
+       AARCH64_HWCAP2_SVEBITPERM)                                          \
+  LINE(AARCH64_SVESHA3, svesha3, "svesha3", 0, AARCH64_HWCAP2_SVESHA3)     \
+  LINE(AARCH64_SVESM4, svesm4, "svesm4", 0, AARCH64_HWCAP2_SVESM4)         \
+  LINE(AARCH64_FLAGM2, flagm2, "flagm2", 0, AARCH64_HWCAP2_FLAGM2)         \
+  LINE(AARCH64_FRINT, frint, "frint", 0, AARCH64_HWCAP2_FRINT)             \
+  LINE(AARCH64_SVEI8MM, svei8mm, "svei8mm", 0, AARCH64_HWCAP2_SVEI8MM)     \
+  LINE(AARCH64_SVEF32MM, svef32mm, "svef32mm", 0, AARCH64_HWCAP2_SVEF32MM) \
+  LINE(AARCH64_SVEF64MM, svef64mm, "svef64mm", 0, AARCH64_HWCAP2_SVEF64MM) \
+  LINE(AARCH64_SVEBF16, svebf16, "svebf16", 0, AARCH64_HWCAP2_SVEBF16)     \
+  LINE(AARCH64_I8MM, i8mm, "i8mm", 0, AARCH64_HWCAP2_I8MM)                 \
+  LINE(AARCH64_BF16, bf16, "bf16", 0, AARCH64_HWCAP2_BF16)                 \
+  LINE(AARCH64_DGH, dgh, "dgh", 0, AARCH64_HWCAP2_DGH)                     \
+  LINE(AARCH64_RNG, rng, "rng", 0, AARCH64_HWCAP2_RNG)                     \
+  LINE(AARCH64_BTI, bti, "bti", 0, AARCH64_HWCAP2_BTI)                     \
+  LINE(AARCH64_MTE, mte, "mte", 0, AARCH64_HWCAP2_MTE)
+#define INTROSPECTION_PREFIX Aarch64
+#define INTROSPECTION_ENUM_PREFIX AARCH64
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdbool.h>
+
+#include "internal/bit_utils.h"
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+static bool HandleAarch64Line(const LineResult result,
+                              Aarch64Info* const info) {
+  StringView line = result.line;
+  StringView key, value;
+  if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
+    if (CpuFeatures_StringView_IsEquals(key, str("Features"))) {
+      for (size_t i = 0; i < AARCH64_LAST_; ++i) {
+        kSetters[i](&info->features, CpuFeatures_StringView_HasWord(
+                                         value, kCpuInfoFlags[i], ' '));
+      }
+    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer"))) {
+      info->implementer = CpuFeatures_StringView_ParsePositiveNumber(value);
+    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU variant"))) {
+      info->variant = CpuFeatures_StringView_ParsePositiveNumber(value);
+    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU part"))) {
+      info->part = CpuFeatures_StringView_ParsePositiveNumber(value);
+    } else if (CpuFeatures_StringView_IsEquals(key, str("CPU revision"))) {
+      info->revision = CpuFeatures_StringView_ParsePositiveNumber(value);
+    }
+  }
+  return !result.eof;
+}
+
+static void FillProcCpuInfoData(Aarch64Info* const info) {
+  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandleAarch64Line(StackLineReader_NextLine(&reader), info)) {
+        break;
+      }
+    }
+    CpuFeatures_CloseFile(fd);
+  }
+}
+
+static const Aarch64Info kEmptyAarch64Info;
+
+Aarch64Info GetAarch64Info(void) {
+  // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
+  // have some information if the executable is sandboxed (aka no access to
+  // /proc/cpuinfo).
+  Aarch64Info info = kEmptyAarch64Info;
+
+  FillProcCpuInfoData(&info);
+  const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
+  for (size_t i = 0; i < AARCH64_LAST_; ++i) {
+    if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
+      kSetters[i](&info.features, true);
+    }
+  }
+
+  return info;
+}
+
+#endif  // defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+#endif  // CPU_FEATURES_ARCH_AARCH64
diff --git a/src/cpuinfo_arm.c b/src/impl_arm_linux_or_android.c
similarity index 65%
rename from src/cpuinfo_arm.c
rename to src/impl_arm_linux_or_android.c
index 0f216bf..997ef93 100644
--- a/src/cpuinfo_arm.c
+++ b/src/impl_arm_linux_or_android.c
@@ -12,50 +12,60 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_ARM
+#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+
 #include "cpuinfo_arm.h"
 
-#include <assert.h>
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                                        \
+  LINE(ARM_SWP, swp, "swp", ARM_HWCAP_SWP, 0)                      \
+  LINE(ARM_HALF, half, "half", ARM_HWCAP_HALF, 0)                  \
+  LINE(ARM_THUMB, thumb, "thumb", ARM_HWCAP_THUMB, 0)              \
+  LINE(ARM_26BIT, _26bit, "26bit", ARM_HWCAP_26BIT, 0)             \
+  LINE(ARM_FASTMULT, fastmult, "fastmult", ARM_HWCAP_FAST_MULT, 0) \
+  LINE(ARM_FPA, fpa, "fpa", ARM_HWCAP_FPA, 0)                      \
+  LINE(ARM_VFP, vfp, "vfp", ARM_HWCAP_VFP, 0)                      \
+  LINE(ARM_EDSP, edsp, "edsp", ARM_HWCAP_EDSP, 0)                  \
+  LINE(ARM_JAVA, java, "java", ARM_HWCAP_JAVA, 0)                  \
+  LINE(ARM_IWMMXT, iwmmxt, "iwmmxt", ARM_HWCAP_IWMMXT, 0)          \
+  LINE(ARM_CRUNCH, crunch, "crunch", ARM_HWCAP_CRUNCH, 0)          \
+  LINE(ARM_THUMBEE, thumbee, "thumbee", ARM_HWCAP_THUMBEE, 0)      \
+  LINE(ARM_NEON, neon, "neon", ARM_HWCAP_NEON, 0)                  \
+  LINE(ARM_VFPV3, vfpv3, "vfpv3", ARM_HWCAP_VFPV3, 0)              \
+  LINE(ARM_VFPV3D16, vfpv3d16, "vfpv3d16", ARM_HWCAP_VFPV3D16, 0)  \
+  LINE(ARM_TLS, tls, "tls", ARM_HWCAP_TLS, 0)                      \
+  LINE(ARM_VFPV4, vfpv4, "vfpv4", ARM_HWCAP_VFPV4, 0)              \
+  LINE(ARM_IDIVA, idiva, "idiva", ARM_HWCAP_IDIVA, 0)              \
+  LINE(ARM_IDIVT, idivt, "idivt", ARM_HWCAP_IDIVT, 0)              \
+  LINE(ARM_VFPD32, vfpd32, "vfpd32", ARM_HWCAP_VFPD32, 0)          \
+  LINE(ARM_LPAE, lpae, "lpae", ARM_HWCAP_LPAE, 0)                  \
+  LINE(ARM_EVTSTRM, evtstrm, "evtstrm", ARM_HWCAP_EVTSTRM, 0)      \
+  LINE(ARM_AES, aes, "aes", 0, ARM_HWCAP2_AES)                     \
+  LINE(ARM_PMULL, pmull, "pmull", 0, ARM_HWCAP2_PMULL)             \
+  LINE(ARM_SHA1, sha1, "sha1", 0, ARM_HWCAP2_SHA1)                 \
+  LINE(ARM_SHA2, sha2, "sha2", 0, ARM_HWCAP2_SHA2)                 \
+  LINE(ARM_CRC32, crc32, "crc32", 0, ARM_HWCAP2_CRC32)
+#define INTROSPECTION_PREFIX Arm
+#define INTROSPECTION_ENUM_PREFIX ARM
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
+
 #include <ctype.h>
+#include <stdbool.h>
 
 #include "internal/bit_utils.h"
 #include "internal/filesystem.h"
-#include "internal/hwcaps.h"
 #include "internal/stack_line_reader.h"
 #include "internal/string_view.h"
 
-// Generation of feature's getters/setters functions and kGetters, kSetters,
-// kCpuInfoFlags and kHardwareCapabilities global tables.
-#define DEFINE_TABLE_FEATURES                                         \
-  FEATURE(ARM_SWP, swp, "swp", ARM_HWCAP_SWP, 0)                      \
-  FEATURE(ARM_HALF, half, "half", ARM_HWCAP_HALF, 0)                  \
-  FEATURE(ARM_THUMB, thumb, "thumb", ARM_HWCAP_THUMB, 0)              \
-  FEATURE(ARM_26BIT, _26bit, "26bit", ARM_HWCAP_26BIT, 0)             \
-  FEATURE(ARM_FASTMULT, fastmult, "fastmult", ARM_HWCAP_FAST_MULT, 0) \
-  FEATURE(ARM_FPA, fpa, "fpa", ARM_HWCAP_FPA, 0)                      \
-  FEATURE(ARM_VFP, vfp, "vfp", ARM_HWCAP_VFP, 0)                      \
-  FEATURE(ARM_EDSP, edsp, "edsp", ARM_HWCAP_EDSP, 0)                  \
-  FEATURE(ARM_JAVA, java, "java", ARM_HWCAP_JAVA, 0)                  \
-  FEATURE(ARM_IWMMXT, iwmmxt, "iwmmxt", ARM_HWCAP_IWMMXT, 0)          \
-  FEATURE(ARM_CRUNCH, crunch, "crunch", ARM_HWCAP_CRUNCH, 0)          \
-  FEATURE(ARM_THUMBEE, thumbee, "thumbee", ARM_HWCAP_THUMBEE, 0)      \
-  FEATURE(ARM_NEON, neon, "neon", ARM_HWCAP_NEON, 0)                  \
-  FEATURE(ARM_VFPV3, vfpv3, "vfpv3", ARM_HWCAP_VFPV3, 0)              \
-  FEATURE(ARM_VFPV3D16, vfpv3d16, "vfpv3d16", ARM_HWCAP_VFPV3D16, 0)  \
-  FEATURE(ARM_TLS, tls, "tls", ARM_HWCAP_TLS, 0)                      \
-  FEATURE(ARM_VFPV4, vfpv4, "vfpv4", ARM_HWCAP_VFPV4, 0)              \
-  FEATURE(ARM_IDIVA, idiva, "idiva", ARM_HWCAP_IDIVA, 0)              \
-  FEATURE(ARM_IDIVT, idivt, "idivt", ARM_HWCAP_IDIVT, 0)              \
-  FEATURE(ARM_VFPD32, vfpd32, "vfpd32", ARM_HWCAP_VFPD32, 0)          \
-  FEATURE(ARM_LPAE, lpae, "lpae", ARM_HWCAP_LPAE, 0)                  \
-  FEATURE(ARM_EVTSTRM, evtstrm, "evtstrm", ARM_HWCAP_EVTSTRM, 0)      \
-  FEATURE(ARM_AES, aes, "aes", 0, ARM_HWCAP2_AES)                     \
-  FEATURE(ARM_PMULL, pmull, "pmull", 0, ARM_HWCAP2_PMULL)             \
-  FEATURE(ARM_SHA1, sha1, "sha1", 0, ARM_HWCAP2_SHA1)                 \
-  FEATURE(ARM_SHA2, sha2, "sha2", 0, ARM_HWCAP2_SHA2)                 \
-  FEATURE(ARM_CRC32, crc32, "crc32", 0, ARM_HWCAP2_CRC32)
-#define DEFINE_TABLE_FEATURE_TYPE ArmFeatures
-#include "define_tables.h"
-
 typedef struct {
   bool processor_reports_armv6;
   bool hardware_reports_goldfish;
@@ -77,8 +87,8 @@
   if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
     if (CpuFeatures_StringView_IsEquals(key, str("Features"))) {
       for (size_t i = 0; i < ARM_LAST_; ++i) {
-        kSetters[i](&info->features,
-                    CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
+        kSetters[i](&info->features, CpuFeatures_StringView_HasWord(
+                                         value, kCpuInfoFlags[i], ' '));
       }
     } else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer"))) {
       info->implementer = CpuFeatures_StringView_ParsePositiveNumber(value);
@@ -142,13 +152,14 @@
       // https://crbug.com/341598.
       info->features.neon = false;
       break;
-    case 0x510006F2:
-    case 0x510006F3:
-      // The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report
-      // IDIV support.
-      info->features.idiva = true;
-      info->features.idivt = true;
-      break;
+  }
+
+  // Some Qualcomm Krait kernels forget to report IDIV support.
+  // https://github.com/torvalds/linux/commit/120ecfafabec382c4feb79ff159ef42a39b6d33b
+  if (info->implementer == 0x51 && info->architecture == 7 &&
+      (info->part == 0x4d || info->part == 0x6f)) {
+    info->features.idiva = true;
+    info->features.idivt = true;
   }
 
   // Propagate cpu features.
@@ -197,16 +208,5 @@
   return info;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Introspection functions
-
-int GetArmFeaturesEnumValue(const ArmFeatures* features,
-                            ArmFeaturesEnum value) {
-  if (value >= ARM_LAST_) return false;
-  return kGetters[value](features);
-}
-
-const char* GetArmFeaturesEnumName(ArmFeaturesEnum value) {
-  if (value >= ARM_LAST_) return "unknown feature";
-  return kCpuInfoFlags[value];
-}
+#endif  // defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+#endif  // CPU_FEATURES_ARCH_ARM
diff --git a/src/cpuinfo_mips.c b/src/impl_mips_linux_or_android.c
similarity index 68%
rename from src/cpuinfo_mips.c
rename to src/impl_mips_linux_or_android.c
index 83e959f..9a3dc2f 100644
--- a/src/cpuinfo_mips.c
+++ b/src/impl_mips_linux_or_android.c
@@ -1,5 +1,3 @@
-// Copyright 2017 Google LLC
-//
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -12,24 +10,33 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_MIPS
+#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+
 #include "cpuinfo_mips.h"
 
-#include <assert.h>
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                     \
+  LINE(MIPS_MSA, msa, "msa", MIPS_HWCAP_MSA, 0) \
+  LINE(MIPS_EVA, eva, "eva", 0, 0)              \
+  LINE(MIPS_R6, r6, "r6", MIPS_HWCAP_R6, 0)
+#define INTROSPECTION_PREFIX Mips
+#define INTROSPECTION_ENUM_PREFIX MIPS
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
 
 #include "internal/filesystem.h"
 #include "internal/hwcaps.h"
 #include "internal/stack_line_reader.h"
 #include "internal/string_view.h"
 
-// Generation of feature's getters/setters functions and kGetters, kSetters,
-// kCpuInfoFlags and kHardwareCapabilities global tables.
-#define DEFINE_TABLE_FEATURES                      \
-  FEATURE(MIPS_MSA, msa, "msa", MIPS_HWCAP_MSA, 0) \
-  FEATURE(MIPS_EVA, eva, "eva", 0, 0)              \
-  FEATURE(MIPS_R6, r6, "r6", MIPS_HWCAP_R6, 0)
-#define DEFINE_TABLE_FEATURE_TYPE MipsFeatures
-#include "define_tables.h"
-
 static bool HandleMipsLine(const LineResult result,
                            MipsFeatures* const features) {
   StringView key, value;
@@ -37,8 +44,8 @@
   if (CpuFeatures_StringView_GetAttributeKeyValue(result.line, &key, &value)) {
     if (CpuFeatures_StringView_IsEquals(key, str("ASEs implemented"))) {
       for (size_t i = 0; i < MIPS_LAST_; ++i) {
-        kSetters[i](features,
-                    CpuFeatures_StringView_HasWord(value, kCpuInfoFlags[i]));
+        kSetters[i](features, CpuFeatures_StringView_HasWord(
+                                  value, kCpuInfoFlags[i], ' '));
       }
     }
   }
@@ -77,16 +84,5 @@
   return info;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Introspection functions
-
-int GetMipsFeaturesEnumValue(const MipsFeatures* features,
-                             MipsFeaturesEnum value) {
-  if (value >= MIPS_LAST_) return false;
-  return kGetters[value](features);
-}
-
-const char* GetMipsFeaturesEnumName(MipsFeaturesEnum value) {
-  if (value >= MIPS_LAST_) return "unknown feature";
-  return kCpuInfoFlags[value];
-}
+#endif  //  defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+#endif  // CPU_FEATURES_ARCH_MIPS
diff --git a/src/impl_ppc_linux.c b/src/impl_ppc_linux.c
new file mode 100644
index 0000000..13a381a
--- /dev/null
+++ b/src/impl_ppc_linux.c
@@ -0,0 +1,162 @@
+// Copyright 2018 IBM.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_PPC
+#ifdef CPU_FEATURES_OS_LINUX
+
+#include "cpuinfo_ppc.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                                                    \
+  LINE(PPC_32, ppc32, "ppc32", PPC_FEATURE_32, 0)                              \
+  LINE(PPC_64, ppc64, "ppc64", PPC_FEATURE_64, 0)                              \
+  LINE(PPC_601_INSTR, ppc601, "ppc601", PPC_FEATURE_601_INSTR, 0)              \
+  LINE(PPC_HAS_ALTIVEC, altivec, "altivec", PPC_FEATURE_HAS_ALTIVEC, 0)        \
+  LINE(PPC_HAS_FPU, fpu, "fpu", PPC_FEATURE_HAS_FPU, 0)                        \
+  LINE(PPC_HAS_MMU, mmu, "mmu", PPC_FEATURE_HAS_MMU, 0)                        \
+  LINE(PPC_HAS_4xxMAC, mac_4xx, "4xxmac", PPC_FEATURE_HAS_4xxMAC, 0)           \
+  LINE(PPC_UNIFIED_CACHE, unifiedcache, "ucache", PPC_FEATURE_UNIFIED_CACHE,   \
+       0)                                                                      \
+  LINE(PPC_HAS_SPE, spe, "spe", PPC_FEATURE_HAS_SPE, 0)                        \
+  LINE(PPC_HAS_EFP_SINGLE, efpsingle, "efpsingle", PPC_FEATURE_HAS_EFP_SINGLE, \
+       0)                                                                      \
+  LINE(PPC_HAS_EFP_DOUBLE, efpdouble, "efpdouble", PPC_FEATURE_HAS_EFP_DOUBLE, \
+       0)                                                                      \
+  LINE(PPC_NO_TB, no_tb, "notb", PPC_FEATURE_NO_TB, 0)                         \
+  LINE(PPC_POWER4, power4, "power4", PPC_FEATURE_POWER4, 0)                    \
+  LINE(PPC_POWER5, power5, "power5", PPC_FEATURE_POWER5, 0)                    \
+  LINE(PPC_POWER5_PLUS, power5plus, "power5+", PPC_FEATURE_POWER5_PLUS, 0)     \
+  LINE(PPC_CELL, cell, "cellbe", PPC_FEATURE_CELL, 0)                          \
+  LINE(PPC_BOOKE, booke, "booke", PPC_FEATURE_BOOKE, 0)                        \
+  LINE(PPC_SMT, smt, "smt", PPC_FEATURE_SMT, 0)                                \
+  LINE(PPC_ICACHE_SNOOP, icachesnoop, "ic_snoop", PPC_FEATURE_ICACHE_SNOOP, 0) \
+  LINE(PPC_ARCH_2_05, arch205, "arch_2_05", PPC_FEATURE_ARCH_2_05, 0)          \
+  LINE(PPC_PA6T, pa6t, "pa6t", PPC_FEATURE_PA6T, 0)                            \
+  LINE(PPC_HAS_DFP, dfp, "dfp", PPC_FEATURE_HAS_DFP, 0)                        \
+  LINE(PPC_POWER6_EXT, power6ext, "power6x", PPC_FEATURE_POWER6_EXT, 0)        \
+  LINE(PPC_ARCH_2_06, arch206, "arch_2_06", PPC_FEATURE_ARCH_2_06, 0)          \
+  LINE(PPC_HAS_VSX, vsx, "vsx", PPC_FEATURE_HAS_VSX, 0)                        \
+  LINE(PPC_PSERIES_PERFMON_COMPAT, pseries_perfmon_compat, "archpmu",          \
+       PPC_FEATURE_PSERIES_PERFMON_COMPAT, 0)                                  \
+  LINE(PPC_TRUE_LE, truele, "true_le", PPC_FEATURE_TRUE_LE, 0)                 \
+  LINE(PPC_PPC_LE, ppcle, "ppcle", PPC_FEATURE_PPC_LE, 0)                      \
+  LINE(PPC_ARCH_2_07, arch207, "arch_2_07", 0, PPC_FEATURE2_ARCH_2_07)         \
+  LINE(PPC_HTM, htm, "htm", 0, PPC_FEATURE2_HTM)                               \
+  LINE(PPC_DSCR, dscr, "dscr", 0, PPC_FEATURE2_DSCR)                           \
+  LINE(PPC_EBB, ebb, "ebb", 0, PPC_FEATURE2_EBB)                               \
+  LINE(PPC_ISEL, isel, "isel", 0, PPC_FEATURE2_ISEL)                           \
+  LINE(PPC_TAR, tar, "tar", 0, PPC_FEATURE2_TAR)                               \
+  LINE(PPC_VEC_CRYPTO, vcrypto, "vcrypto", 0, PPC_FEATURE2_VEC_CRYPTO)         \
+  LINE(PPC_HTM_NOSC, htm_nosc, "htm-nosc", 0, PPC_FEATURE2_HTM_NOSC)           \
+  LINE(PPC_ARCH_3_00, arch300, "arch_3_00", 0, PPC_FEATURE2_ARCH_3_00)         \
+  LINE(PPC_HAS_IEEE128, ieee128, "ieee128", 0, PPC_FEATURE2_HAS_IEEE128)       \
+  LINE(PPC_DARN, darn, "darn", 0, PPC_FEATURE2_DARN)                           \
+  LINE(PPC_SCV, scv, "scv", 0, PPC_FEATURE2_SCV)                               \
+  LINE(PPC_HTM_NO_SUSPEND, htm_no_suspend, "htm-no-suspend", 0,                \
+       PPC_FEATURE2_HTM_NO_SUSPEND)
+#define INTROSPECTION_PREFIX PPC
+#define INTROSPECTION_ENUM_PREFIX PPC
+#include "define_introspection_and_hwcaps.inl"
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdbool.h>
+
+#include "internal/bit_utils.h"
+#include "internal/filesystem.h"
+#include "internal/hwcaps.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+static bool HandlePPCLine(const LineResult result,
+                          PPCPlatformStrings* const strings) {
+  StringView line = result.line;
+  StringView key, value;
+  if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
+    if (CpuFeatures_StringView_HasWord(key, "platform", ' ')) {
+      CpuFeatures_StringView_CopyString(value, strings->platform,
+                                        sizeof(strings->platform));
+    } else if (CpuFeatures_StringView_IsEquals(key, str("model"))) {
+      CpuFeatures_StringView_CopyString(value, strings->model,
+                                        sizeof(strings->platform));
+    } else if (CpuFeatures_StringView_IsEquals(key, str("machine"))) {
+      CpuFeatures_StringView_CopyString(value, strings->machine,
+                                        sizeof(strings->platform));
+    } else if (CpuFeatures_StringView_IsEquals(key, str("cpu"))) {
+      CpuFeatures_StringView_CopyString(value, strings->cpu,
+                                        sizeof(strings->platform));
+    }
+  }
+  return !result.eof;
+}
+
+static void FillProcCpuInfoData(PPCPlatformStrings* const strings) {
+  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (;;) {
+      if (!HandlePPCLine(StackLineReader_NextLine(&reader), strings)) {
+        break;
+      }
+    }
+    CpuFeatures_CloseFile(fd);
+  }
+}
+
+static const PPCInfo kEmptyPPCInfo;
+
+PPCInfo GetPPCInfo(void) {
+  /*
+   * On Power feature flags aren't currently in cpuinfo so we only look at
+   * the auxilary vector.
+   */
+  PPCInfo info = kEmptyPPCInfo;
+  const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
+  for (size_t i = 0; i < PPC_LAST_; ++i) {
+    if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
+      kSetters[i](&info.features, true);
+    }
+  }
+  return info;
+}
+
+static const PPCPlatformStrings kEmptyPPCPlatformStrings;
+
+PPCPlatformStrings GetPPCPlatformStrings(void) {
+  PPCPlatformStrings strings = kEmptyPPCPlatformStrings;
+  const char* platform = CpuFeatures_GetPlatformPointer();
+  const char* base_platform = CpuFeatures_GetBasePlatformPointer();
+
+  FillProcCpuInfoData(&strings);
+
+  if (platform != NULL)
+    CpuFeatures_StringView_CopyString(str(platform), strings.type.platform,
+                                      sizeof(strings.type.platform));
+  if (base_platform != NULL)
+    CpuFeatures_StringView_CopyString(str(base_platform),
+                                      strings.type.base_platform,
+                                      sizeof(strings.type.base_platform));
+
+  return strings;
+}
+
+#endif  // CPU_FEATURES_OS_LINUX
+#endif  // CPU_FEATURES_ARCH_PPC
diff --git a/src/cpuinfo_x86.c b/src/impl_x86__base_implementation.inl
similarity index 67%
rename from src/cpuinfo_x86.c
rename to src/impl_x86__base_implementation.inl
index 378ed05..09f24b2 100644
--- a/src/cpuinfo_x86.c
+++ b/src/impl_x86__base_implementation.inl
@@ -13,11 +13,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cpuinfo_x86.h"
-
 #include <stdbool.h>
 #include <string.h>
 
+#include "copy.inl"
+#include "cpuinfo_x86.h"
+#include "equals.inl"
 #include "internal/bit_utils.h"
 #include "internal/cpuid_x86.h"
 
@@ -25,91 +26,6 @@
 #error "Cannot compile cpuinfo_x86 on a non x86 platform."
 #endif
 
-// Generation of feature's getters/setters functions and kGetters, kSetters,
-// kCpuInfoFlags global tables.
-#define DEFINE_TABLE_FEATURES                                                  \
-  FEATURE(X86_FPU, fpu, "fpu", 0, 0)                                           \
-  FEATURE(X86_TSC, tsc, "tsc", 0, 0)                                           \
-  FEATURE(X86_CX8, cx8, "cx8", 0, 0)                                           \
-  FEATURE(X86_CLFSH, clfsh, "clfsh", 0, 0)                                     \
-  FEATURE(X86_MMX, mmx, "mmx", 0, 0)                                           \
-  FEATURE(X86_AES, aes, "aes", 0, 0)                                           \
-  FEATURE(X86_ERMS, erms, "erms", 0, 0)                                        \
-  FEATURE(X86_F16C, f16c, "f16c", 0, 0)                                        \
-  FEATURE(X86_FMA4, fma4, "fma4", 0, 0)                                        \
-  FEATURE(X86_FMA3, fma3, "fma3", 0, 0)                                        \
-  FEATURE(X86_VAES, vaes, "vaes", 0, 0)                                        \
-  FEATURE(X86_VPCLMULQDQ, vpclmulqdq, "vpclmulqdq", 0, 0)                      \
-  FEATURE(X86_BMI1, bmi1, "bmi1", 0, 0)                                        \
-  FEATURE(X86_HLE, hle, "hle", 0, 0)                                           \
-  FEATURE(X86_BMI2, bmi2, "bmi2", 0, 0)                                        \
-  FEATURE(X86_RTM, rtm, "rtm", 0, 0)                                           \
-  FEATURE(X86_RDSEED, rdseed, "rdseed", 0, 0)                                  \
-  FEATURE(X86_CLFLUSHOPT, clflushopt, "clflushopt", 0, 0)                      \
-  FEATURE(X86_CLWB, clwb, "clwb", 0, 0)                                        \
-  FEATURE(X86_SSE, sse, "sse", 0, 0)                                           \
-  FEATURE(X86_SSE2, sse2, "sse2", 0, 0)                                        \
-  FEATURE(X86_SSE3, sse3, "sse3", 0, 0)                                        \
-  FEATURE(X86_SSSE3, ssse3, "ssse3", 0, 0)                                     \
-  FEATURE(X86_SSE4_1, sse4_1, "sse4_1", 0, 0)                                  \
-  FEATURE(X86_SSE4_2, sse4_2, "sse4_2", 0, 0)                                  \
-  FEATURE(X86_SSE4A, sse4a, "sse4a", 0, 0)                                     \
-  FEATURE(X86_AVX, avx, "avx", 0, 0)                                           \
-  FEATURE(X86_AVX2, avx2, "avx2", 0, 0)                                        \
-  FEATURE(X86_AVX512F, avx512f, "avx512f", 0, 0)                               \
-  FEATURE(X86_AVX512CD, avx512cd, "avx512cd", 0, 0)                            \
-  FEATURE(X86_AVX512ER, avx512er, "avx512er", 0, 0)                            \
-  FEATURE(X86_AVX512PF, avx512pf, "avx512pf", 0, 0)                            \
-  FEATURE(X86_AVX512BW, avx512bw, "avx512bw", 0, 0)                            \
-  FEATURE(X86_AVX512DQ, avx512dq, "avx512dq", 0, 0)                            \
-  FEATURE(X86_AVX512VL, avx512vl, "avx512vl", 0, 0)                            \
-  FEATURE(X86_AVX512IFMA, avx512ifma, "avx512ifma", 0, 0)                      \
-  FEATURE(X86_AVX512VBMI, avx512vbmi, "avx512vbmi", 0, 0)                      \
-  FEATURE(X86_AVX512VBMI2, avx512vbmi2, "avx512vbmi2", 0, 0)                   \
-  FEATURE(X86_AVX512VNNI, avx512vnni, "avx512vnni", 0, 0)                      \
-  FEATURE(X86_AVX512BITALG, avx512bitalg, "avx512bitalg", 0, 0)                \
-  FEATURE(X86_AVX512VPOPCNTDQ, avx512vpopcntdq, "avx512vpopcntdq", 0, 0)       \
-  FEATURE(X86_AVX512_4VNNIW, avx512_4vnniw, "avx512_4vnniw", 0, 0)             \
-  FEATURE(X86_AVX512_4VBMI2, avx512_4vbmi2, "avx512_4vbmi2", 0, 0)             \
-  FEATURE(X86_AVX512_SECOND_FMA, avx512_second_fma, "avx512_second_fma", 0, 0) \
-  FEATURE(X86_AVX512_4FMAPS, avx512_4fmaps, "avx512_4fmaps", 0, 0)             \
-  FEATURE(X86_AVX512_BF16, avx512_bf16, "avx512_bf16", 0, 0)                   \
-  FEATURE(X86_AVX512_VP2INTERSECT, avx512_vp2intersect, "avx512_vp2intersect", \
-          0, 0)                                                                \
-  FEATURE(X86_AMX_BF16, amx_bf16, "amx_bf16", 0, 0)                            \
-  FEATURE(X86_AMX_TILE, amx_tile, "amx_tile", 0, 0)                            \
-  FEATURE(X86_AMX_INT8, amx_int8, "amx_int8", 0, 0)                            \
-  FEATURE(X86_PCLMULQDQ, pclmulqdq, "pclmulqdq", 0, 0)                         \
-  FEATURE(X86_SMX, smx, "smx", 0, 0)                                           \
-  FEATURE(X86_SGX, sgx, "sgx", 0, 0)                                           \
-  FEATURE(X86_CX16, cx16, "cx16", 0, 0)                                        \
-  FEATURE(X86_SHA, sha, "sha", 0, 0)                                           \
-  FEATURE(X86_POPCNT, popcnt, "popcnt", 0, 0)                                  \
-  FEATURE(X86_MOVBE, movbe, "movbe", 0, 0)                                     \
-  FEATURE(X86_RDRND, rdrnd, "rdrnd", 0, 0)                                     \
-  FEATURE(X86_DCA, dca, "dca", 0, 0)                                           \
-  FEATURE(X86_SS, ss, "ss", 0, 0)
-#define DEFINE_TABLE_FEATURE_TYPE X86Features
-#define DEFINE_TABLE_DONT_GENERATE_HWCAPS
-#include "define_tables.h"
-
-// The following includes are necessary to provide SSE detections on pre-AVX
-// microarchitectures.
-#if defined(CPU_FEATURES_OS_WINDOWS)
-#include <windows.h>  // IsProcessorFeaturePresent
-#elif defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
-#include "internal/filesystem.h"         // Needed to parse /proc/cpuinfo
-#include "internal/stack_line_reader.h"  // Needed to parse /proc/cpuinfo
-#include "internal/string_view.h"        // Needed to parse /proc/cpuinfo
-#elif defined(CPU_FEATURES_OS_DARWIN)
-#if !defined(HAVE_SYSCTLBYNAME)
-#error "Darwin needs support for sysctlbyname"
-#endif
-#include <sys/sysctl.h>
-#else
-#error "Unsupported OS"
-#endif  // CPU_FEATURES_OS
-
 ////////////////////////////////////////////////////////////////////////////////
 // Definitions for CpuId and GetXCR0Eax.
 ////////////////////////////////////////////////////////////////////////////////
@@ -157,8 +73,6 @@
 #error "Unsupported compiler, x86 cpuid requires either GCC, Clang or MSVC."
 #endif
 
-static Leaf CpuId(uint32_t leaf_id) { return GetCpuidLeaf(leaf_id, 0); }
-
 static const Leaf kEmptyLeaf;
 
 static Leaf SafeCpuIdEx(uint32_t max_cpuid_leaf, uint32_t leaf_id, int ecx) {
@@ -169,10 +83,47 @@
   }
 }
 
-static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
-  return SafeCpuIdEx(max_cpuid_leaf, leaf_id, 0);
+typedef struct {
+  uint32_t max_cpuid_leaf;
+  Leaf leaf_0;    // Root
+  Leaf leaf_1;    // Family, Model, Stepping
+  Leaf leaf_2;    // Intel cache info + features
+  Leaf leaf_7;    // Features
+  Leaf leaf_7_1;  // Features
+  uint32_t max_cpuid_leaf_ext;
+  Leaf leaf_80000000;  // Root for extended leaves
+  Leaf leaf_80000001;  // AMD features features and cache
+  Leaf leaf_80000002;  // brand string
+  Leaf leaf_80000003;  // brand string
+  Leaf leaf_80000004;  // brand string
+} Leaves;
+
+static Leaves ReadLeaves() {
+  const Leaf leaf_0 = GetCpuidLeaf(0, 0);
+  const uint32_t max_cpuid_leaf = leaf_0.eax;
+  const Leaf leaf_80000000 = GetCpuidLeaf(0x80000000, 0);
+  const uint32_t max_cpuid_leaf_ext = leaf_80000000.eax;
+  return (Leaves){
+      .max_cpuid_leaf = max_cpuid_leaf,
+      .leaf_0 = leaf_0,
+      .leaf_1 = SafeCpuIdEx(max_cpuid_leaf, 0x00000001, 0),
+      .leaf_2 = SafeCpuIdEx(max_cpuid_leaf, 0x00000002, 0),
+      .leaf_7 = SafeCpuIdEx(max_cpuid_leaf, 0x00000007, 0),
+      .leaf_7_1 = SafeCpuIdEx(max_cpuid_leaf, 0x00000007, 1),
+      .max_cpuid_leaf_ext = max_cpuid_leaf_ext,
+      .leaf_80000000 = leaf_80000000,
+      .leaf_80000001 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000001, 0),
+      .leaf_80000002 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000002, 0),
+      .leaf_80000003 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000003, 0),
+      .leaf_80000004 = SafeCpuIdEx(max_cpuid_leaf_ext, 0x80000004, 0),
+  };
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// OS support
+// TODO: Add documentation
+////////////////////////////////////////////////////////////////////////////////
+
 #define MASK_XMM 0x2
 #define MASK_YMM 0x4
 #define MASK_MASKREG 0x20
@@ -211,34 +162,9 @@
                                MASK_ZMM16_31 | MASK_XTILECFG | MASK_XTILEDATA);
 }
 
-static bool HasSecondFMA(uint32_t model) {
-  // Skylake server
-  if (model == 0x55) {
-    char proc_name[49] = {0};
-    FillX86BrandString(proc_name);
-    // detect Xeon
-    if (proc_name[9] == 'X') {
-      // detect Silver or Bronze
-      if (proc_name[17] == 'S' || proc_name[17] == 'B') return false;
-      // detect Gold 5_20 and below, except for Gold 53__
-      if (proc_name[17] == 'G' && proc_name[22] == '5')
-        return ((proc_name[23] == '3') ||
-                (proc_name[24] == '2' && proc_name[25] == '2'));
-      // detect Xeon W 210x
-      if (proc_name[17] == 'W' && proc_name[21] == '0') return false;
-      // detect Xeon D 2xxx
-      if (proc_name[17] == 'D' && proc_name[19] == '2' && proc_name[20] == '1')
-        return false;
-    }
-    return true;
-  }
-  // Cannon Lake client
-  if (model == 0x66) return false;
-  // Ice Lake client
-  if (model == 0x7d || model == 0x7e) return false;
-  // This is the right default...
-  return true;
-}
+////////////////////////////////////////////////////////////////////////////////
+// Vendor
+////////////////////////////////////////////////////////////////////////////////
 
 static void SetVendor(const Leaf leaf, char* const vendor) {
   *(uint32_t*)(vendor) = leaf.ebx;
@@ -254,6 +180,556 @@
   return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
 }
 
+static int IsVendorByX86Info(const X86Info* info, const char* const name) {
+  return equals(info->vendor, name, sizeof(info->vendor));
+}
+
+// TODO: Remove when deprecation period is over,
+void FillX86BrandString(char brand_string[49]) {
+  const Leaves leaves = ReadLeaves();
+  const Leaf packed[3] = {
+      leaves.leaf_80000002,
+      leaves.leaf_80000003,
+      leaves.leaf_80000004,
+  };
+#if __STDC_VERSION__ >= 201112L
+  _Static_assert(sizeof(packed) == 48, "Leaves must be packed");
+#endif
+  copy(brand_string, (const char*)(packed), 48);
+  brand_string[48] = '\0';
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CpuId
+////////////////////////////////////////////////////////////////////////////////
+
+static bool HasSecondFMA(const X86Info* info) {
+  // Skylake server
+  if (info->model == 0x55) {
+    // detect Xeon
+    if (info->brand_string[9] == 'X') {
+      // detect Silver or Bronze
+      if (info->brand_string[17] == 'S' || info->brand_string[17] == 'B')
+        return false;
+      // detect Gold 5_20 and below, except for Gold 53__
+      if (info->brand_string[17] == 'G' && info->brand_string[22] == '5')
+        return (
+            (info->brand_string[23] == '3') ||
+            (info->brand_string[24] == '2' && info->brand_string[25] == '2'));
+      // detect Xeon W 210x
+      if (info->brand_string[17] == 'W' && info->brand_string[21] == '0')
+        return false;
+      // detect Xeon D 2xxx
+      if (info->brand_string[17] == 'D' && info->brand_string[19] == '2' &&
+          info->brand_string[20] == '1')
+        return false;
+    }
+    return true;
+  }
+  // Cannon Lake client
+  if (info->model == 0x66) return false;
+  // Ice Lake client
+  if (info->model == 0x7d || info->model == 0x7e) return false;
+  // This is the right default...
+  return true;
+}
+
+// Internal structure to hold the OS support for vector operations.
+// Avoid to recompute them since each call to cpuid is ~100 cycles.
+typedef struct {
+  bool sse_registers;
+  bool avx_registers;
+  bool avx512_registers;
+  bool amx_registers;
+} OsPreserves;
+
+// These two functions have to be implemented by the OS, that is the file
+// including this file.
+static void OverrideOsPreserves(OsPreserves* os_preserves);
+static void DetectFeaturesFromOs(X86Info* info, X86Features* features);
+
+// Reference https://en.wikipedia.org/wiki/CPUID.
+static void ParseCpuId(const Leaves* leaves, X86Info* info,
+                       OsPreserves* os_preserves) {
+  const Leaf leaf_1 = leaves->leaf_1;
+  const Leaf leaf_7 = leaves->leaf_7;
+  const Leaf leaf_7_1 = leaves->leaf_7_1;
+
+  const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
+  const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
+  const bool have_xcr0 = have_xsave && have_osxsave;
+
+  const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
+  const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
+  const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
+  const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
+
+  X86Features* const features = &info->features;
+
+  // Fill Family, Model and Stepping.
+  info->family = extended_family + family;
+  info->model = (extended_model << 4) + model;
+  info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
+
+  // Fill Brand String.
+  const Leaf packed[3] = {
+      leaves->leaf_80000002,
+      leaves->leaf_80000003,
+      leaves->leaf_80000004,
+  };
+#if __STDC_VERSION__ >= 201112L
+  _Static_assert(sizeof(packed) == 48, "Leaves must be packed");
+#endif
+  copy(info->brand_string, (const char*)(packed), 48);
+  info->brand_string[48] = '\0';
+
+  // Fill cpu features.
+  features->fpu = IsBitSet(leaf_1.edx, 0);
+  features->tsc = IsBitSet(leaf_1.edx, 4);
+  features->cx8 = IsBitSet(leaf_1.edx, 8);
+  features->clfsh = IsBitSet(leaf_1.edx, 19);
+  features->mmx = IsBitSet(leaf_1.edx, 23);
+  features->ss = IsBitSet(leaf_1.edx, 27);
+  features->pclmulqdq = IsBitSet(leaf_1.ecx, 1);
+  features->smx = IsBitSet(leaf_1.ecx, 6);
+  features->cx16 = IsBitSet(leaf_1.ecx, 13);
+  features->dca = IsBitSet(leaf_1.ecx, 18);
+  features->movbe = IsBitSet(leaf_1.ecx, 22);
+  features->popcnt = IsBitSet(leaf_1.ecx, 23);
+  features->aes = IsBitSet(leaf_1.ecx, 25);
+  features->f16c = IsBitSet(leaf_1.ecx, 29);
+  features->rdrnd = IsBitSet(leaf_1.ecx, 30);
+  features->sgx = IsBitSet(leaf_7.ebx, 2);
+  features->bmi1 = IsBitSet(leaf_7.ebx, 3);
+  features->hle = IsBitSet(leaf_7.ebx, 4);
+  features->bmi2 = IsBitSet(leaf_7.ebx, 8);
+  features->erms = IsBitSet(leaf_7.ebx, 9);
+  features->rtm = IsBitSet(leaf_7.ebx, 11);
+  features->rdseed = IsBitSet(leaf_7.ebx, 18);
+  features->clflushopt = IsBitSet(leaf_7.ebx, 23);
+  features->clwb = IsBitSet(leaf_7.ebx, 24);
+  features->sha = IsBitSet(leaf_7.ebx, 29);
+  features->vaes = IsBitSet(leaf_7.ecx, 9);
+  features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
+  features->adx = IsBitSet(leaf_7.ebx, 19);
+
+  /////////////////////////////////////////////////////////////////////////////
+  // The following section is devoted to Vector Extensions.
+  /////////////////////////////////////////////////////////////////////////////
+
+  // CPU with AVX expose XCR0 which enables checking vector extensions OS
+  // support through cpuid.
+  if (have_xcr0) {
+    // Here we rely exclusively on cpuid for both CPU and OS support of vector
+    // extensions.
+    const uint32_t xcr0_eax = GetXCR0Eax();
+    os_preserves->sse_registers = HasXmmOsXSave(xcr0_eax);
+    os_preserves->avx_registers = HasYmmOsXSave(xcr0_eax);
+    os_preserves->avx512_registers = HasZmmOsXSave(xcr0_eax);
+    os_preserves->amx_registers = HasTmmOsXSave(xcr0_eax);
+    OverrideOsPreserves(os_preserves);
+
+    if (os_preserves->sse_registers) {
+      features->sse = IsBitSet(leaf_1.edx, 25);
+      features->sse2 = IsBitSet(leaf_1.edx, 26);
+      features->sse3 = IsBitSet(leaf_1.ecx, 0);
+      features->ssse3 = IsBitSet(leaf_1.ecx, 9);
+      features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
+      features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
+    }
+    if (os_preserves->avx_registers) {
+      features->fma3 = IsBitSet(leaf_1.ecx, 12);
+      features->avx = IsBitSet(leaf_1.ecx, 28);
+      features->avx2 = IsBitSet(leaf_7.ebx, 5);
+    }
+    if (os_preserves->avx512_registers) {
+      features->avx512f = IsBitSet(leaf_7.ebx, 16);
+      features->avx512cd = IsBitSet(leaf_7.ebx, 28);
+      features->avx512er = IsBitSet(leaf_7.ebx, 27);
+      features->avx512pf = IsBitSet(leaf_7.ebx, 26);
+      features->avx512bw = IsBitSet(leaf_7.ebx, 30);
+      features->avx512dq = IsBitSet(leaf_7.ebx, 17);
+      features->avx512vl = IsBitSet(leaf_7.ebx, 31);
+      features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
+      features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
+      features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
+      features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
+      features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
+      features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
+      features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
+      features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
+      features->avx512_second_fma = HasSecondFMA(info);
+      features->avx512_4fmaps = IsBitSet(leaf_7.edx, 3);
+      features->avx512_bf16 = IsBitSet(leaf_7_1.eax, 5);
+      features->avx512_vp2intersect = IsBitSet(leaf_7.edx, 8);
+    }
+    if (os_preserves->amx_registers) {
+      features->amx_bf16 = IsBitSet(leaf_7.edx, 22);
+      features->amx_tile = IsBitSet(leaf_7.edx, 24);
+      features->amx_int8 = IsBitSet(leaf_7.edx, 25);
+    }
+  } else {
+    // When XCR0 is not available (Atom based or older cpus) we need to defer to
+    // the OS via custom code.
+    DetectFeaturesFromOs(info, features);
+    // Now that we have queried the OS for SSE support, we report this back to
+    // os_preserves. This is needed in case of AMD CPU's to enable testing of
+    // sse4a (See ParseExtraAMDCpuId below).
+    if (features->sse) os_preserves->sse_registers = true;
+  }
+}
+
+static void ParseExtraAMDCpuId(const Leaves* leaves, X86Info* info,
+                               OsPreserves os_preserves) {
+  const Leaf leaf_80000001 = leaves->leaf_80000001;
+
+  X86Features* const features = &info->features;
+
+  if (os_preserves.sse_registers) {
+    features->sse4a = IsBitSet(leaf_80000001.ecx, 6);
+  }
+
+  if (os_preserves.avx_registers) {
+    features->fma4 = IsBitSet(leaf_80000001.ecx, 16);
+  }
+}
+
+static const X86Info kEmptyX86Info;
+static const OsPreserves kEmptyOsPreserves;
+
+X86Info GetX86Info(void) {
+  X86Info info = kEmptyX86Info;
+  const Leaves leaves = ReadLeaves();
+  const bool is_intel =
+      IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_GENUINE_INTEL);
+  const bool is_amd =
+      IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_AUTHENTIC_AMD);
+  const bool is_hygon =
+      IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_HYGON_GENUINE);
+  const bool is_zhaoxin = 
+      (IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_CENTAUR_HAULS) || 
+       IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_SHANGHAI));
+  SetVendor(leaves.leaf_0, info.vendor);
+  if (is_intel || is_amd || is_hygon || is_zhaoxin) {
+    OsPreserves os_preserves = kEmptyOsPreserves;
+    ParseCpuId(&leaves, &info, &os_preserves);
+    if (is_amd || is_hygon) {
+      ParseExtraAMDCpuId(&leaves, &info, os_preserves);
+    }
+  }
+  return info;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Microarchitecture
+////////////////////////////////////////////////////////////////////////////////
+
+#define CPUID(FAMILY, MODEL) ((((FAMILY)&0xFF) << 8) | ((MODEL)&0xFF))
+
+X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
+  if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_GENUINE_INTEL)) {
+    switch (CPUID(info->family, info->model)) {
+      case CPUID(0x04, 0x01):
+      case CPUID(0x04, 0x02):
+      case CPUID(0x04, 0x03):
+      case CPUID(0x04, 0x04):
+      case CPUID(0x04, 0x05):
+      case CPUID(0x04, 0x07):
+      case CPUID(0x04, 0x08):
+      case CPUID(0x04, 0x09):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/80486
+        return INTEL_80486;
+      case CPUID(0x05, 0x01):
+      case CPUID(0x05, 0x02):
+      case CPUID(0x05, 0x04):
+      case CPUID(0x05, 0x07):
+      case CPUID(0x05, 0x08):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/p5
+        return INTEL_P5;
+      case CPUID(0x05, 0x09):
+      case CPUID(0x05, 0x0A):
+        // https://en.wikichip.org/wiki/intel/quark
+        return INTEL_LAKEMONT;
+      case CPUID(0x06, 0x1C):  // Intel(R) Atom(TM) CPU 230 @ 1.60GHz
+      case CPUID(0x06, 0x35):
+      case CPUID(0x06, 0x36):
+      case CPUID(0x06, 0x70):  // https://en.wikichip.org/wiki/intel/atom/230
+        // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
+        return INTEL_ATOM_BNL;
+      case CPUID(0x06, 0x37):
+      case CPUID(0x06, 0x4C):
+        // https://en.wikipedia.org/wiki/Silvermont
+        return INTEL_ATOM_SMT;
+      case CPUID(0x06, 0x5C):
+        // https://en.wikipedia.org/wiki/Goldmont
+        return INTEL_ATOM_GMT;
+      case CPUID(0x06, 0x0F):
+      case CPUID(0x06, 0x16):
+        // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
+        return INTEL_CORE;
+      case CPUID(0x06, 0x17):
+      case CPUID(0x06, 0x1D):
+        // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
+        return INTEL_PNR;
+      case CPUID(0x06, 0x1A):
+      case CPUID(0x06, 0x1E):
+      case CPUID(0x06, 0x1F):
+      case CPUID(0x06, 0x2E):
+        // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
+        return INTEL_NHM;
+      case CPUID(0x06, 0x25):
+      case CPUID(0x06, 0x2C):
+      case CPUID(0x06, 0x2F):
+        // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
+        return INTEL_WSM;
+      case CPUID(0x06, 0x2A):
+      case CPUID(0x06, 0x2D):
+        // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
+        return INTEL_SNB;
+      case CPUID(0x06, 0x3A):
+      case CPUID(0x06, 0x3E):
+        // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
+        return INTEL_IVB;
+      case CPUID(0x06, 0x3C):
+      case CPUID(0x06, 0x3F):
+      case CPUID(0x06, 0x45):
+      case CPUID(0x06, 0x46):
+        // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
+        return INTEL_HSW;
+      case CPUID(0x06, 0x3D):
+      case CPUID(0x06, 0x47):
+      case CPUID(0x06, 0x4F):
+      case CPUID(0x06, 0x56):
+        // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
+        return INTEL_BDW;
+      case CPUID(0x06, 0x4E):
+      case CPUID(0x06, 0x55):
+      case CPUID(0x06, 0x5E):
+        // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
+        return INTEL_SKL;
+      case CPUID(0x06, 0x66):
+        // https://en.wikipedia.org/wiki/Cannon_Lake_(microarchitecture)
+        return INTEL_CNL;
+      case CPUID(0x06, 0x7D):  // client
+      case CPUID(0x06, 0x7E):  // client
+      case CPUID(0x06, 0x9D):  // NNP-I
+      case CPUID(0x06, 0x6A):  // server
+      case CPUID(0x06, 0x6C):  // server
+        // https://en.wikipedia.org/wiki/Ice_Lake_(microprocessor)
+        return INTEL_ICL;
+      case CPUID(0x06, 0x8C):
+      case CPUID(0x06, 0x8D):
+        // https://en.wikipedia.org/wiki/Tiger_Lake_(microarchitecture)
+        return INTEL_TGL;
+      case CPUID(0x06, 0x8F):
+        // https://en.wikipedia.org/wiki/Sapphire_Rapids
+        return INTEL_SPR;
+      case CPUID(0x06, 0x8E):
+        switch (info->stepping) {
+          case 9:
+            return INTEL_KBL;  // https://en.wikipedia.org/wiki/Kaby_Lake
+          case 10:
+            return INTEL_CFL;  // https://en.wikipedia.org/wiki/Coffee_Lake
+          case 11:
+            return INTEL_WHL;  // https://en.wikipedia.org/wiki/Whiskey_Lake_(microarchitecture)
+          default:
+            return X86_UNKNOWN;
+        }
+      case CPUID(0x06, 0x9E):
+        if (info->stepping > 9) {
+          // https://en.wikipedia.org/wiki/Coffee_Lake
+          return INTEL_CFL;
+        } else {
+          // https://en.wikipedia.org/wiki/Kaby_Lake
+          return INTEL_KBL;
+        }
+      case CPUID(0x06, 0x97):
+      case CPUID(0x06, 0x9A):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/alder_lake
+        return INTEL_ADL;
+      case CPUID(0x06, 0xA7):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/rocket_lake
+        return INTEL_RCL;
+      case CPUID(0x06, 0x85):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/knights_mill
+        return INTEL_KNIGHTS_M;
+      case CPUID(0x06, 0x57):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/knights_landing
+        return INTEL_KNIGHTS_L;
+      case CPUID(0x0B, 0x00):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/knights_ferry
+        return INTEL_KNIGHTS_F;
+      case CPUID(0x0B, 0x01):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/knights_corner
+        return INTEL_KNIGHTS_C;
+      case CPUID(0x0F, 0x01):
+      case CPUID(0x0F, 0x02):
+      case CPUID(0x0F, 0x03):
+      case CPUID(0x0F, 0x04):
+      case CPUID(0x0F, 0x06):
+        // https://en.wikichip.org/wiki/intel/microarchitectures/netburst
+        return INTEL_NETBURST;
+      default:
+        return X86_UNKNOWN;
+    }
+  }
+  if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_CENTAUR_HAULS)) {
+    switch (CPUID(info->family, info->model)) {
+      case CPUID(0x06, 0x0F):
+      case CPUID(0x06, 0x19):
+        // https://en.wikichip.org/wiki/zhaoxin/microarchitectures/zhangjiang
+        return ZHAOXIN_ZHANGJIANG;
+      case CPUID(0x07, 0x1B):
+	// https://en.wikichip.org/wiki/zhaoxin/microarchitectures/wudaokou
+	return ZHAOXIN_WUDAOKOU;
+      case CPUID(0x07, 0x3B):
+	// https://en.wikichip.org/wiki/zhaoxin/microarchitectures/lujiazui
+	return ZHAOXIN_LUJIAZUI;
+      case CPUID(0x07, 0x5B):
+	return ZHAOXIN_YONGFENG;
+      default:
+	return X86_UNKNOWN;
+    }
+  }
+  if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_SHANGHAI)) {
+    switch (CPUID(info->family, info->model)) {
+      case CPUID(0x06, 0x0F):
+      case CPUID(0x06, 0x19):
+        // https://en.wikichip.org/wiki/zhaoxin/microarchitectures/zhangjiang
+        return ZHAOXIN_ZHANGJIANG;
+      case CPUID(0x07, 0x1B):
+	// https://en.wikichip.org/wiki/zhaoxin/microarchitectures/wudaokou
+	return ZHAOXIN_WUDAOKOU;
+      case CPUID(0x07, 0x3B):
+	// https://en.wikichip.org/wiki/zhaoxin/microarchitectures/lujiazui
+	return ZHAOXIN_LUJIAZUI;
+      case CPUID(0x07, 0x5B):
+	return ZHAOXIN_YONGFENG;
+      default:
+	return X86_UNKNOWN;
+    }
+  }
+  if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_AUTHENTIC_AMD)) {
+    switch (CPUID(info->family, info->model)) {
+      // https://en.wikichip.org/wiki/amd/cpuid
+      case CPUID(0xF, 0x04):
+      case CPUID(0xF, 0x05):
+      case CPUID(0xF, 0x07):
+      case CPUID(0xF, 0x08):
+      case CPUID(0xF, 0x0C):
+      case CPUID(0xF, 0x0E):
+      case CPUID(0xF, 0x0F):
+      case CPUID(0xF, 0x14):
+      case CPUID(0xF, 0x15):
+      case CPUID(0xF, 0x17):
+      case CPUID(0xF, 0x18):
+      case CPUID(0xF, 0x1B):
+      case CPUID(0xF, 0x1C):
+      case CPUID(0xF, 0x1F):
+      case CPUID(0xF, 0x21):
+      case CPUID(0xF, 0x23):
+      case CPUID(0xF, 0x24):
+      case CPUID(0xF, 0x25):
+      case CPUID(0xF, 0x27):
+      case CPUID(0xF, 0x2B):
+      case CPUID(0xF, 0x2C):
+      case CPUID(0xF, 0x2F):
+      case CPUID(0xF, 0x41):
+      case CPUID(0xF, 0x43):
+      case CPUID(0xF, 0x48):
+      case CPUID(0xF, 0x4B):
+      case CPUID(0xF, 0x4C):
+      case CPUID(0xF, 0x4F):
+      case CPUID(0xF, 0x5D):
+      case CPUID(0xF, 0x5F):
+      case CPUID(0xF, 0x68):
+      case CPUID(0xF, 0x6B):
+      case CPUID(0xF, 0x6F):
+      case CPUID(0xF, 0x7F):
+      case CPUID(0xF, 0xC1):
+        return AMD_HAMMER;
+      case CPUID(0x10, 0x02):
+      case CPUID(0x10, 0x04):
+      case CPUID(0x10, 0x05):
+      case CPUID(0x10, 0x06):
+      case CPUID(0x10, 0x08):
+      case CPUID(0x10, 0x09):
+      case CPUID(0x10, 0x0A):
+        return AMD_K10;
+      case CPUID(0x11, 0x03):
+        // http://developer.amd.com/wordpress/media/2012/10/41788.pdf
+        return AMD_K11;
+      case CPUID(0x12, 0x01):
+        // https://www.amd.com/system/files/TechDocs/44739_12h_Rev_Gd.pdf
+        return AMD_K12;
+      case CPUID(0x14, 0x00):
+      case CPUID(0x14, 0x01):
+      case CPUID(0x14, 0x02):
+        // https://www.amd.com/system/files/TechDocs/47534_14h_Mod_00h-0Fh_Rev_Guide.pdf
+        return AMD_BOBCAT;
+      case CPUID(0x15, 0x01):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/bulldozer
+        return AMD_BULLDOZER;
+      case CPUID(0x15, 0x02):
+      case CPUID(0x15, 0x11):
+      case CPUID(0x15, 0x13):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/piledriver
+        return AMD_PILEDRIVER;
+      case CPUID(0x15, 0x30):
+      case CPUID(0x15, 0x38):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/steamroller
+        return AMD_STREAMROLLER;
+      case CPUID(0x15, 0x60):
+      case CPUID(0x15, 0x65):
+      case CPUID(0x15, 0x70):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/excavator
+        return AMD_EXCAVATOR;
+      case CPUID(0x16, 0x00):
+        return AMD_JAGUAR;
+      case CPUID(0x16, 0x30):
+        return AMD_PUMA;
+      case CPUID(0x17, 0x01):
+      case CPUID(0x17, 0x11):
+      case CPUID(0x17, 0x18):
+      case CPUID(0x17, 0x20):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/zen
+        return AMD_ZEN;
+      case CPUID(0x17, 0x08):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/zen%2B
+        return AMD_ZEN_PLUS;
+      case CPUID(0x17, 0x31):
+      case CPUID(0x17, 0x47):
+      case CPUID(0x17, 0x60):
+      case CPUID(0x17, 0x68):
+      case CPUID(0x17, 0x71):
+      case CPUID(0x17, 0x90):
+      case CPUID(0x17, 0x98):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/zen_2
+        return AMD_ZEN2;
+      case CPUID(0x19, 0x01):
+      case CPUID(0x19, 0x21):
+      case CPUID(0x19, 0x30):
+      case CPUID(0x19, 0x40):
+      case CPUID(0x19, 0x50):
+        // https://en.wikichip.org/wiki/amd/microarchitectures/zen_3
+        return AMD_ZEN3;
+      default:
+        return X86_UNKNOWN;
+    }
+  }
+  if (IsVendorByX86Info(info, CPU_FEATURES_VENDOR_HYGON_GENUINE)) {
+    switch (CPUID(info->family, info->model)) {
+      case CPUID(0x18, 0x00):
+        return AMD_ZEN;
+    }
+  }
+  return X86_UNKNOWN;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CacheInfo
+////////////////////////////////////////////////////////////////////////////////
+
 static const CacheLevelInfo kEmptyCacheLevelInfo;
 
 static CacheLevelInfo GetCacheLevelInfo(const uint32_t reg) {
@@ -1114,509 +1590,219 @@
   }
 }
 
-static void GetByteArrayFromRegister(uint32_t result[4], const uint32_t reg) {
-  for (int i = 0; i < 4; ++i) {
-    result[i] = ExtractBitRange(reg, (i + 1) * 8, i * 8);
-  }
-}
+// From https://www.felixcloutier.com/x86/cpuid#tbl-3-12
+static void ParseLeaf2(const Leaves* leaves, CacheInfo* info) {
+  Leaf leaf = leaves->leaf_2;
+  // The least-significant byte in register EAX (register AL) will always return
+  // 01H. Software should ignore this value and not interpret it as an
+  // informational descriptor.
+  leaf.eax &= 0xFFFFFF00;  // Zeroing out AL. 0 is the empty descriptor.
+  // The most significant bit (bit 31) of each register indicates whether the
+  // register contains valid information (set to 0) or is reserved (set to 1).
+  if (IsBitSet(leaf.eax, 31)) leaf.eax = 0;
+  if (IsBitSet(leaf.ebx, 31)) leaf.ebx = 0;
+  if (IsBitSet(leaf.ecx, 31)) leaf.ecx = 0;
+  if (IsBitSet(leaf.edx, 31)) leaf.edx = 0;
 
-static void ParseLeaf2(const int max_cpuid_leaf, CacheInfo* info) {
-  Leaf leaf = SafeCpuId(max_cpuid_leaf, 2);
-  uint32_t registers[] = {leaf.eax, leaf.ebx, leaf.ecx, leaf.edx};
-  for (int i = 0; i < 4; ++i) {
-    if (registers[i] & (1U << 31)) {
-      continue;  // register does not contains valid information
-    }
-    uint32_t bytes[4];
-    GetByteArrayFromRegister(bytes, registers[i]);
-    for (int j = 0; j < 4; ++j) {
-      if (bytes[j] == 0xFF)
-        break;  // leaf 4 should be used to fetch cache information
-      info->levels[info->size] = GetCacheLevelInfo(bytes[j]);
-    }
+  uint8_t data[16];
+#if __STDC_VERSION__ >= 201112L
+  _Static_assert(sizeof(Leaf) == sizeof(data), "Leaf must be 16 bytes");
+#endif
+  copy((char*)(data), (const char*)(&leaf), sizeof(data));
+  for (size_t i = 0; i < sizeof(data); ++i) {
+    const uint8_t descriptor = data[i];
+    if (descriptor == 0) continue;
+    info->levels[info->size] = GetCacheLevelInfo(descriptor);
     info->size++;
   }
 }
 
-static void ParseLeaf4(const int max_cpuid_leaf, CacheInfo* info) {
-  info->size = 0;
-  for (int cache_id = 0; cache_id < CPU_FEATURES_MAX_CACHE_LEVEL; cache_id++) {
-    const Leaf leaf = SafeCpuIdEx(max_cpuid_leaf, 4, cache_id);
-    CacheType cache_type = ExtractBitRange(leaf.eax, 4, 0);
-    if (cache_type == CPU_FEATURE_CACHE_NULL) {
-      info->levels[cache_id] = kEmptyCacheLevelInfo;
-      continue;
-    }
+static const CacheInfo kEmptyCacheInfo;
+
+// For newer Intel CPUs uses "CPUID, eax=0x00000004".
+// https://www.felixcloutier.com/x86/cpuid#input-eax-=-04h--returns-deterministic-cache-parameters-for-each-level
+// For newer AMD CPUs uses "CPUID, eax=0x8000001D"
+static void ParseCacheInfo(const int max_cpuid_leaf, uint32_t leaf_id,
+                           CacheInfo* old_info) {
+  CacheInfo info = kEmptyCacheInfo;
+  for (int index = 0; info.size < CPU_FEATURES_MAX_CACHE_LEVEL; ++index) {
+    const Leaf leaf = SafeCpuIdEx(max_cpuid_leaf, leaf_id, index);
+    int cache_type_field = ExtractBitRange(leaf.eax, 4, 0);
+    CacheType cache_type;
+    if (cache_type_field == 0)
+      break;
+    else if (cache_type_field == 1)
+      cache_type = CPU_FEATURE_CACHE_DATA;
+    else if (cache_type_field == 2)
+      cache_type = CPU_FEATURE_CACHE_INSTRUCTION;
+    else if (cache_type_field == 3)
+      cache_type = CPU_FEATURE_CACHE_UNIFIED;
+    else
+      break;  // Should not occur as per documentation.
     int level = ExtractBitRange(leaf.eax, 7, 5);
     int line_size = ExtractBitRange(leaf.ebx, 11, 0) + 1;
     int partitioning = ExtractBitRange(leaf.ebx, 21, 12) + 1;
     int ways = ExtractBitRange(leaf.ebx, 31, 22) + 1;
     int tlb_entries = leaf.ecx + 1;
-    int cache_size = (ways * partitioning * line_size * (tlb_entries));
-    info->levels[cache_id] = (CacheLevelInfo){.level = level,
+    int cache_size = ways * partitioning * line_size * tlb_entries;
+    info.levels[info.size] = (CacheLevelInfo){.level = level,
                                               .cache_type = cache_type,
                                               .cache_size = cache_size,
                                               .ways = ways,
                                               .line_size = line_size,
                                               .tlb_entries = tlb_entries,
                                               .partitioning = partitioning};
-    info->size++;
+    ++info.size;
   }
-}
-
-// Internal structure to hold the OS support for vector operations.
-// Avoid to recompute them since each call to cpuid is ~100 cycles.
-typedef struct {
-  bool have_sse_via_os;
-  bool have_sse_via_cpuid;
-  bool have_avx;
-  bool have_avx512;
-  bool have_amx;
-} OsSupport;
-
-static const OsSupport kEmptyOsSupport;
-
-static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) {
-  const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
-  const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
-  const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
-  const bool have_xcr0 = have_xsave && have_osxsave;
-
-  OsSupport os_support = kEmptyOsSupport;
-
-  if (have_xcr0) {
-    // AVX capable cpu will expose XCR0.
-    const uint32_t xcr0_eax = GetXCR0Eax();
-    os_support.have_sse_via_cpuid = HasXmmOsXSave(xcr0_eax);
-    os_support.have_avx = HasYmmOsXSave(xcr0_eax);
-    os_support.have_avx512 = HasZmmOsXSave(xcr0_eax);
-    os_support.have_amx = HasTmmOsXSave(xcr0_eax);
-  } else {
-    // Atom based or older cpus need to ask the OS for sse support.
-    os_support.have_sse_via_os = true;
-  }
-
-  return os_support;
-}
-
-#if defined(CPU_FEATURES_OS_WINDOWS)
-#if defined(CPU_FEATURES_MOCK_CPUID_X86)
-extern bool GetWindowsIsProcessorFeaturePresent(DWORD);
-#else  // CPU_FEATURES_MOCK_CPUID_X86
-static bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
-  return IsProcessorFeaturePresent(ProcessorFeature);
-}
-#endif
-#endif  // CPU_FEATURES_OS_WINDOWS
-
-#if defined(CPU_FEATURES_OS_DARWIN)
-#if defined(CPU_FEATURES_MOCK_CPUID_X86)
-extern bool GetDarwinSysCtlByName(const char*);
-#else  // CPU_FEATURES_MOCK_CPUID_X86
-static bool GetDarwinSysCtlByName(const char* name) {
-  int enabled;
-  size_t enabled_len = sizeof(enabled);
-  const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0);
-  return failure ? false : enabled;
-}
-#endif
-#endif  // CPU_FEATURES_OS_DARWIN
-
-static void DetectSseViaOs(X86Features* features) {
-#if defined(CPU_FEATURES_OS_WINDOWS)
-  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
-  features->sse =
-      GetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
-  features->sse2 =
-      GetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
-  features->sse3 =
-      GetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
-#elif defined(CPU_FEATURES_OS_DARWIN)
-  // Handling Darwin platform through sysctlbyname.
-  features->sse = GetDarwinSysCtlByName("hw.optional.sse");
-  features->sse2 = GetDarwinSysCtlByName("hw.optional.sse2");
-  features->sse3 = GetDarwinSysCtlByName("hw.optional.sse3");
-  features->ssse3 = GetDarwinSysCtlByName("hw.optional.supplementalsse3");
-  features->sse4_1 = GetDarwinSysCtlByName("hw.optional.sse4_1");
-  features->sse4_2 = GetDarwinSysCtlByName("hw.optional.sse4_2");
-#elif defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
-  // Handling Linux platform through /proc/cpuinfo.
-  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
-  if (fd >= 0) {
-    StackLineReader reader;
-    StackLineReader_Initialize(&reader, fd);
-    for (;;) {
-      const LineResult result = StackLineReader_NextLine(&reader);
-      const StringView line = result.line;
-      StringView key, value;
-      if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
-        if (CpuFeatures_StringView_IsEquals(key, str("flags"))) {
-          features->sse = CpuFeatures_StringView_HasWord(value, "sse");
-          features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2");
-          features->sse3 = CpuFeatures_StringView_HasWord(value, "sse3");
-          features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3");
-          features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1");
-          features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2");
-          break;
-        }
-      }
-      if (result.eof) break;
-    }
-    CpuFeatures_CloseFile(fd);
-  }
-#else
-#error "Unsupported fallback detection of SSE OS support."
-#endif
-}
-
-// Reference https://en.wikipedia.org/wiki/CPUID.
-static void ParseCpuId(const uint32_t max_cpuid_leaf,
-                       const OsSupport os_support, X86Info* info) {
-  const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
-  const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
-  const Leaf leaf_7_1 = SafeCpuIdEx(max_cpuid_leaf, 7, 1);
-
-  const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
-  const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
-  const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
-  const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
-
-  X86Features* const features = &info->features;
-
-  info->family = extended_family + family;
-  info->model = (extended_model << 4) + model;
-  info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
-
-  features->fpu = IsBitSet(leaf_1.edx, 0);
-  features->tsc = IsBitSet(leaf_1.edx, 4);
-  features->cx8 = IsBitSet(leaf_1.edx, 8);
-  features->clfsh = IsBitSet(leaf_1.edx, 19);
-  features->mmx = IsBitSet(leaf_1.edx, 23);
-  features->ss = IsBitSet(leaf_1.edx, 27);
-  features->pclmulqdq = IsBitSet(leaf_1.ecx, 1);
-  features->smx = IsBitSet(leaf_1.ecx, 6);
-  features->cx16 = IsBitSet(leaf_1.ecx, 13);
-  features->dca = IsBitSet(leaf_1.ecx, 18);
-  features->movbe = IsBitSet(leaf_1.ecx, 22);
-  features->popcnt = IsBitSet(leaf_1.ecx, 23);
-  features->aes = IsBitSet(leaf_1.ecx, 25);
-  features->f16c = IsBitSet(leaf_1.ecx, 29);
-  features->rdrnd = IsBitSet(leaf_1.ecx, 30);
-  features->sgx = IsBitSet(leaf_7.ebx, 2);
-  features->bmi1 = IsBitSet(leaf_7.ebx, 3);
-  features->hle = IsBitSet(leaf_7.ebx, 4);
-  features->bmi2 = IsBitSet(leaf_7.ebx, 8);
-  features->erms = IsBitSet(leaf_7.ebx, 9);
-  features->rtm = IsBitSet(leaf_7.ebx, 11);
-  features->rdseed = IsBitSet(leaf_7.ebx, 18);
-  features->clflushopt = IsBitSet(leaf_7.ebx, 23);
-  features->clwb = IsBitSet(leaf_7.ebx, 24);
-  features->sha = IsBitSet(leaf_7.ebx, 29);
-  features->vaes = IsBitSet(leaf_7.ecx, 9);
-  features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
-
-  if (os_support.have_sse_via_os) {
-    DetectSseViaOs(features);
-  } else if (os_support.have_sse_via_cpuid) {
-    features->sse = IsBitSet(leaf_1.edx, 25);
-    features->sse2 = IsBitSet(leaf_1.edx, 26);
-    features->sse3 = IsBitSet(leaf_1.ecx, 0);
-    features->ssse3 = IsBitSet(leaf_1.ecx, 9);
-    features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
-    features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
-  }
-
-  if (os_support.have_avx) {
-    features->fma3 = IsBitSet(leaf_1.ecx, 12);
-    features->avx = IsBitSet(leaf_1.ecx, 28);
-    features->avx2 = IsBitSet(leaf_7.ebx, 5);
-  }
-
-  if (os_support.have_avx512) {
-    features->avx512f = IsBitSet(leaf_7.ebx, 16);
-    features->avx512cd = IsBitSet(leaf_7.ebx, 28);
-    features->avx512er = IsBitSet(leaf_7.ebx, 27);
-    features->avx512pf = IsBitSet(leaf_7.ebx, 26);
-    features->avx512bw = IsBitSet(leaf_7.ebx, 30);
-    features->avx512dq = IsBitSet(leaf_7.ebx, 17);
-    features->avx512vl = IsBitSet(leaf_7.ebx, 31);
-    features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
-    features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
-    features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
-    features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
-    features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
-    features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
-    features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
-    features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
-    features->avx512_second_fma = HasSecondFMA(info->model);
-    features->avx512_4fmaps = IsBitSet(leaf_7.edx, 3);
-    features->avx512_bf16 = IsBitSet(leaf_7_1.eax, 5);
-    features->avx512_vp2intersect = IsBitSet(leaf_7.edx, 8);
-  }
-
-  if (os_support.have_amx) {
-    features->amx_bf16 = IsBitSet(leaf_7.edx, 22);
-    features->amx_tile = IsBitSet(leaf_7.edx, 24);
-    features->amx_int8 = IsBitSet(leaf_7.edx, 25);
-  }
-}
-
-// Reference
-// https://en.wikipedia.org/wiki/CPUID#EAX=80000000h:_Get_Highest_Extended_Function_Implemented.
-static void ParseExtraAMDCpuId(X86Info* info, OsSupport os_support) {
-  const Leaf leaf_80000000 = CpuId(0x80000000);
-  const uint32_t max_extended_cpuid_leaf = leaf_80000000.eax;
-  const Leaf leaf_80000001 = SafeCpuId(max_extended_cpuid_leaf, 0x80000001);
-
-  X86Features* const features = &info->features;
-
-  if (os_support.have_sse_via_cpuid) {
-    features->sse4a = IsBitSet(leaf_80000001.ecx, 6);
-  }
-
-  if (os_support.have_avx) {
-    features->fma4 = IsBitSet(leaf_80000001.ecx, 16);
-  }
-}
-
-static const X86Info kEmptyX86Info;
-static const CacheInfo kEmptyCacheInfo;
-
-X86Info GetX86Info(void) {
-  X86Info info = kEmptyX86Info;
-  const Leaf leaf_0 = CpuId(0);
-  const bool is_intel = IsVendor(leaf_0, "GenuineIntel");
-  const bool is_amd = IsVendor(leaf_0, "AuthenticAMD");
-  SetVendor(leaf_0, info.vendor);
-  if (is_intel || is_amd) {
-    const uint32_t max_cpuid_leaf = leaf_0.eax;
-    const OsSupport os_support = CheckOsSupport(max_cpuid_leaf);
-    ParseCpuId(max_cpuid_leaf, os_support, &info);
-    if (is_amd) {
-      ParseExtraAMDCpuId(&info, os_support);
-    }
-  }
-  return info;
+  // Override CacheInfo if we successfully extracted Deterministic Cache
+  // Parameters.
+  if (info.size > 0) *old_info = info;
 }
 
 CacheInfo GetX86CacheInfo(void) {
   CacheInfo info = kEmptyCacheInfo;
-  const Leaf leaf_0 = CpuId(0);
-  const uint32_t max_cpuid_leaf = leaf_0.eax;
-  if (IsVendor(leaf_0, "GenuineIntel")) {
-    ParseLeaf2(max_cpuid_leaf, &info);
-    ParseLeaf4(max_cpuid_leaf, &info);
+  const Leaves leaves = ReadLeaves();
+  if (IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_GENUINE_INTEL) ||
+      IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_CENTAUR_HAULS) ||
+      IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_SHANGHAI)) {
+    ParseLeaf2(&leaves, &info);
+    ParseCacheInfo(leaves.max_cpuid_leaf, 4, &info);
+  } else if (IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_AUTHENTIC_AMD) ||
+             IsVendor(leaves.leaf_0, CPU_FEATURES_VENDOR_HYGON_GENUINE)) {
+    // If CPUID Fn8000_0001_ECX[TopologyExtensions]==0
+    // then CPUID Fn8000_0001_E[D,C,B,A]X is reserved.
+    // https://www.amd.com/system/files/TechDocs/25481.pdf
+    if (IsBitSet(leaves.leaf_80000001.ecx, 22)) {
+      ParseCacheInfo(leaves.max_cpuid_leaf_ext, 0x8000001D, &info);
+    }
   }
   return info;
 }
 
-#define CPUID(FAMILY, MODEL) ((((FAMILY)&0xFF) << 8) | ((MODEL)&0xFF))
-
-X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
-  if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
-    switch (CPUID(info->family, info->model)) {
-      case CPUID(0x06, 0x35):
-      case CPUID(0x06, 0x36):
-        // https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
-        return INTEL_ATOM_BNL;
-      case CPUID(0x06, 0x37):
-      case CPUID(0x06, 0x4C):
-        // https://en.wikipedia.org/wiki/Silvermont
-        return INTEL_ATOM_SMT;
-      case CPUID(0x06, 0x5C):
-        // https://en.wikipedia.org/wiki/Goldmont
-        return INTEL_ATOM_GMT;
-      case CPUID(0x06, 0x0F):
-      case CPUID(0x06, 0x16):
-        // https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
-        return INTEL_CORE;
-      case CPUID(0x06, 0x17):
-      case CPUID(0x06, 0x1D):
-        // https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
-        return INTEL_PNR;
-      case CPUID(0x06, 0x1A):
-      case CPUID(0x06, 0x1E):
-      case CPUID(0x06, 0x1F):
-      case CPUID(0x06, 0x2E):
-        // https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
-        return INTEL_NHM;
-      case CPUID(0x06, 0x25):
-      case CPUID(0x06, 0x2C):
-      case CPUID(0x06, 0x2F):
-        // https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
-        return INTEL_WSM;
-      case CPUID(0x06, 0x2A):
-      case CPUID(0x06, 0x2D):
-        // https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
-        return INTEL_SNB;
-      case CPUID(0x06, 0x3A):
-      case CPUID(0x06, 0x3E):
-        // https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
-        return INTEL_IVB;
-      case CPUID(0x06, 0x3C):
-      case CPUID(0x06, 0x3F):
-      case CPUID(0x06, 0x45):
-      case CPUID(0x06, 0x46):
-        // https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
-        return INTEL_HSW;
-      case CPUID(0x06, 0x3D):
-      case CPUID(0x06, 0x47):
-      case CPUID(0x06, 0x4F):
-      case CPUID(0x06, 0x56):
-        // https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
-        return INTEL_BDW;
-      case CPUID(0x06, 0x4E):
-      case CPUID(0x06, 0x55):
-      case CPUID(0x06, 0x5E):
-        // https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
-        return INTEL_SKL;
-      case CPUID(0x06, 0x66):
-        // https://en.wikipedia.org/wiki/Cannon_Lake_(microarchitecture)
-        return INTEL_CNL;
-      case CPUID(0x06, 0x7D):  // client
-      case CPUID(0x06, 0x7E):  // client
-      case CPUID(0x06, 0x9D):  // NNP-I
-      case CPUID(0x06, 0x6A):  // server
-      case CPUID(0x06, 0x6C):  // server
-        // https://en.wikipedia.org/wiki/Ice_Lake_(microprocessor)
-        return INTEL_ICL;
-      case CPUID(0x06, 0x8C):
-      case CPUID(0x06, 0x8D):
-        // https://en.wikipedia.org/wiki/Tiger_Lake_(microarchitecture)
-        return INTEL_TGL;
-      case CPUID(0x06, 0x8F):
-        // https://en.wikipedia.org/wiki/Sapphire_Rapids
-        return INTEL_SPR;
-      case CPUID(0x06, 0x8E):
-        switch (info->stepping) {
-          case 9:
-            return INTEL_KBL;  // https://en.wikipedia.org/wiki/Kaby_Lake
-          case 10:
-            return INTEL_CFL;  // https://en.wikipedia.org/wiki/Coffee_Lake
-          case 11:
-            return INTEL_WHL;  // https://en.wikipedia.org/wiki/Whiskey_Lake_(microarchitecture)
-          default:
-            return X86_UNKNOWN;
-        }
-      case CPUID(0x06, 0x9E):
-        if (info->stepping > 9) {
-          // https://en.wikipedia.org/wiki/Coffee_Lake
-          return INTEL_CFL;
-        } else {
-          // https://en.wikipedia.org/wiki/Kaby_Lake
-          return INTEL_KBL;
-        }
-      default:
-        return X86_UNKNOWN;
-    }
-  }
-  if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
-    switch (info->family) {
-        // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
-      case 0x0F:
-        return AMD_HAMMER;
-      case 0x10:
-        return AMD_K10;
-      case 0x14:
-        return AMD_BOBCAT;
-      case 0x15:
-        return AMD_BULLDOZER;
-      case 0x16:
-        return AMD_JAGUAR;
-      case 0x17:
-        return AMD_ZEN;
-      default:
-        return X86_UNKNOWN;
-    }
-  }
-  return X86_UNKNOWN;
-}
-
-static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
-                      char* buffer) {
-  const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
-  // We allow calling memcpy from SetString which is only called when requesting
-  // X86BrandString.
-  memcpy(buffer, &leaf, sizeof(Leaf));
-}
-
-void FillX86BrandString(char brand_string[49]) {
-  const Leaf leaf_ext_0 = CpuId(0x80000000);
-  const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
-  SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
-  SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
-  SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
-  brand_string[48] = '\0';
-}
-
 ////////////////////////////////////////////////////////////////////////////////
-// Introspection functions
+// Definitions for introspection.
+////////////////////////////////////////////////////////////////////////////////
+#define INTROSPECTION_TABLE                                \
+  LINE(X86_FPU, fpu, , , )                                 \
+  LINE(X86_TSC, tsc, , , )                                 \
+  LINE(X86_CX8, cx8, , , )                                 \
+  LINE(X86_CLFSH, clfsh, , , )                             \
+  LINE(X86_MMX, mmx, , , )                                 \
+  LINE(X86_AES, aes, , , )                                 \
+  LINE(X86_ERMS, erms, , , )                               \
+  LINE(X86_F16C, f16c, , , )                               \
+  LINE(X86_FMA4, fma4, , , )                               \
+  LINE(X86_FMA3, fma3, , , )                               \
+  LINE(X86_VAES, vaes, , , )                               \
+  LINE(X86_VPCLMULQDQ, vpclmulqdq, , , )                   \
+  LINE(X86_BMI1, bmi1, , , )                               \
+  LINE(X86_HLE, hle, , , )                                 \
+  LINE(X86_BMI2, bmi2, , , )                               \
+  LINE(X86_RTM, rtm, , , )                                 \
+  LINE(X86_RDSEED, rdseed, , , )                           \
+  LINE(X86_CLFLUSHOPT, clflushopt, , , )                   \
+  LINE(X86_CLWB, clwb, , , )                               \
+  LINE(X86_SSE, sse, , , )                                 \
+  LINE(X86_SSE2, sse2, , , )                               \
+  LINE(X86_SSE3, sse3, , , )                               \
+  LINE(X86_SSSE3, ssse3, , , )                             \
+  LINE(X86_SSE4_1, sse4_1, , , )                           \
+  LINE(X86_SSE4_2, sse4_2, , , )                           \
+  LINE(X86_SSE4A, sse4a, , , )                             \
+  LINE(X86_AVX, avx, , , )                                 \
+  LINE(X86_AVX2, avx2, , , )                               \
+  LINE(X86_AVX512F, avx512f, , , )                         \
+  LINE(X86_AVX512CD, avx512cd, , , )                       \
+  LINE(X86_AVX512ER, avx512er, , , )                       \
+  LINE(X86_AVX512PF, avx512pf, , , )                       \
+  LINE(X86_AVX512BW, avx512bw, , , )                       \
+  LINE(X86_AVX512DQ, avx512dq, , , )                       \
+  LINE(X86_AVX512VL, avx512vl, , , )                       \
+  LINE(X86_AVX512IFMA, avx512ifma, , , )                   \
+  LINE(X86_AVX512VBMI, avx512vbmi, , , )                   \
+  LINE(X86_AVX512VBMI2, avx512vbmi2, , , )                 \
+  LINE(X86_AVX512VNNI, avx512vnni, , , )                   \
+  LINE(X86_AVX512BITALG, avx512bitalg, , , )               \
+  LINE(X86_AVX512VPOPCNTDQ, avx512vpopcntdq, , , )         \
+  LINE(X86_AVX512_4VNNIW, avx512_4vnniw, , , )             \
+  LINE(X86_AVX512_4VBMI2, avx512_4vbmi2, , , )             \
+  LINE(X86_AVX512_SECOND_FMA, avx512_second_fma, , , )     \
+  LINE(X86_AVX512_4FMAPS, avx512_4fmaps, , , )             \
+  LINE(X86_AVX512_BF16, avx512_bf16, , , )                 \
+  LINE(X86_AVX512_VP2INTERSECT, avx512_vp2intersect, , , ) \
+  LINE(X86_AMX_BF16, amx_bf16, , , )                       \
+  LINE(X86_AMX_TILE, amx_tile, , , )                       \
+  LINE(X86_AMX_INT8, amx_int8, , , )                       \
+  LINE(X86_PCLMULQDQ, pclmulqdq, , , )                     \
+  LINE(X86_SMX, smx, , , )                                 \
+  LINE(X86_SGX, sgx, , , )                                 \
+  LINE(X86_CX16, cx16, , , )                               \
+  LINE(X86_SHA, sha, , , )                                 \
+  LINE(X86_POPCNT, popcnt, , , )                           \
+  LINE(X86_MOVBE, movbe, , , )                             \
+  LINE(X86_RDRND, rdrnd, , , )                             \
+  LINE(X86_DCA, dca, , , )                                 \
+  LINE(X86_SS, ss, , , )                                   \
+  LINE(X86_ADX, adx, , , )
+#define INTROSPECTION_PREFIX X86
+#define INTROSPECTION_ENUM_PREFIX X86
+#include "define_introspection.inl"
 
-int GetX86FeaturesEnumValue(const X86Features* features,
-                            X86FeaturesEnum value) {
-  if (value >= X86_LAST_) return false;
-  return kGetters[value](features);
-}
+#define X86_MICROARCHITECTURE_NAMES \
+  LINE(X86_UNKNOWN)                 \
+  LINE(ZHAOXIN_ZHANGJIANG)          \
+  LINE(ZHAOXIN_WUDAOKOU)            \
+  LINE(ZHAOXIN_LUJIAZUI)            \
+  LINE(ZHAOXIN_YONGFENG)            \
+  LINE(INTEL_80486)                 \
+  LINE(INTEL_P5)                    \
+  LINE(INTEL_LAKEMONT)              \
+  LINE(INTEL_CORE)                  \
+  LINE(INTEL_PNR)                   \
+  LINE(INTEL_NHM)                   \
+  LINE(INTEL_ATOM_BNL)              \
+  LINE(INTEL_WSM)                   \
+  LINE(INTEL_SNB)                   \
+  LINE(INTEL_IVB)                   \
+  LINE(INTEL_ATOM_SMT)              \
+  LINE(INTEL_HSW)                   \
+  LINE(INTEL_BDW)                   \
+  LINE(INTEL_SKL)                   \
+  LINE(INTEL_ATOM_GMT)              \
+  LINE(INTEL_KBL)                   \
+  LINE(INTEL_CFL)                   \
+  LINE(INTEL_WHL)                   \
+  LINE(INTEL_CNL)                   \
+  LINE(INTEL_ICL)                   \
+  LINE(INTEL_TGL)                   \
+  LINE(INTEL_SPR)                   \
+  LINE(INTEL_ADL)                   \
+  LINE(INTEL_RCL)                   \
+  LINE(INTEL_KNIGHTS_M)             \
+  LINE(INTEL_KNIGHTS_L)             \
+  LINE(INTEL_KNIGHTS_F)             \
+  LINE(INTEL_KNIGHTS_C)             \
+  LINE(INTEL_NETBURST)              \
+  LINE(AMD_HAMMER)                  \
+  LINE(AMD_K10)                     \
+  LINE(AMD_K11)                     \
+  LINE(AMD_K12)                     \
+  LINE(AMD_BOBCAT)                  \
+  LINE(AMD_PILEDRIVER)              \
+  LINE(AMD_STREAMROLLER)            \
+  LINE(AMD_EXCAVATOR)               \
+  LINE(AMD_BULLDOZER)               \
+  LINE(AMD_JAGUAR)                  \
+  LINE(AMD_PUMA)                    \
+  LINE(AMD_ZEN)                     \
+  LINE(AMD_ZEN_PLUS)                \
+  LINE(AMD_ZEN2)                    \
+  LINE(AMD_ZEN3)
 
-const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
-  if (value >= X86_LAST_) return "unknown_feature";
-  return kCpuInfoFlags[value];
-}
-
-const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
-  switch (uarch) {
-    case X86_UNKNOWN:
-      return "X86_UNKNOWN";
-    case INTEL_CORE:
-      return "INTEL_CORE";
-    case INTEL_PNR:
-      return "INTEL_PNR";
-    case INTEL_NHM:
-      return "INTEL_NHM";
-    case INTEL_ATOM_BNL:
-      return "INTEL_ATOM_BNL";
-    case INTEL_WSM:
-      return "INTEL_WSM";
-    case INTEL_SNB:
-      return "INTEL_SNB";
-    case INTEL_IVB:
-      return "INTEL_IVB";
-    case INTEL_ATOM_SMT:
-      return "INTEL_ATOM_SMT";
-    case INTEL_HSW:
-      return "INTEL_HSW";
-    case INTEL_BDW:
-      return "INTEL_BDW";
-    case INTEL_SKL:
-      return "INTEL_SKL";
-    case INTEL_ATOM_GMT:
-      return "INTEL_ATOM_GMT";
-    case INTEL_KBL:
-      return "INTEL_KBL";
-    case INTEL_CFL:
-      return "INTEL_CFL";
-    case INTEL_WHL:
-      return "INTEL_WHL";
-    case INTEL_CNL:
-      return "INTEL_CNL";
-    case INTEL_ICL:
-      return "INTEL_ICL";
-    case INTEL_TGL:
-      return "INTEL_TGL";
-    case INTEL_SPR:
-      return "INTEL_SPR";
-    case AMD_HAMMER:
-      return "AMD_HAMMER";
-    case AMD_K10:
-      return "AMD_K10";
-    case AMD_BOBCAT:
-      return "AMD_BOBCAT";
-    case AMD_BULLDOZER:
-      return "AMD_BULLDOZER";
-    case AMD_JAGUAR:
-      return "AMD_JAGUAR";
-    case AMD_ZEN:
-      return "AMD_ZEN";
-  }
-  return "unknown microarchitecture";
+const char* GetX86MicroarchitectureName(X86Microarchitecture value) {
+#define LINE(ENUM) [ENUM] = STRINGIZE(ENUM),
+  static const char* kMicroarchitectureNames[] = {X86_MICROARCHITECTURE_NAMES};
+#undef LINE
+  if (value >= X86_MICROARCHITECTURE_LAST_) return "unknown microarchitecture";
+  return kMicroarchitectureNames[value];
 }
diff --git a/src/impl_x86_freebsd.c b/src/impl_x86_freebsd.c
new file mode 100644
index 0000000..ba6c6e3
--- /dev/null
+++ b/src/impl_x86_freebsd.c
@@ -0,0 +1,68 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_X86
+#ifdef CPU_FEATURES_OS_FREEBSD
+
+#include "impl_x86__base_implementation.inl"
+
+static void OverrideOsPreserves(OsPreserves* os_preserves) {
+  (void)os_preserves;
+  // No override
+}
+
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+static void DetectFeaturesFromOs(X86Info* info, X86Features* features) {
+  (void)info;
+  // Handling FreeBSD platform through parsing /var/run/dmesg.boot.
+  const int fd = CpuFeatures_OpenFile("/var/run/dmesg.boot");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (bool stop = false; !stop;) {
+      const LineResult result = StackLineReader_NextLine(&reader);
+      if (result.eof) stop = true;
+      const StringView line = result.line;
+      if (!CpuFeatures_StringView_StartsWith(line, str("  Features"))) continue;
+      // Lines of interests are of the following form:
+      // "  Features=0x1783fbff<PSE36,MMX,FXSR,SSE,SSE2,HTT>"
+      // We first extract the comma separated values between angle brackets.
+      StringView csv = result.line;
+      int index = CpuFeatures_StringView_IndexOfChar(csv, '<');
+      if (index >= 0) csv = CpuFeatures_StringView_PopFront(csv, index + 1);
+      if (csv.size > 0 && CpuFeatures_StringView_Back(csv) == '>')
+        csv = CpuFeatures_StringView_PopBack(csv, 1);
+      if (CpuFeatures_StringView_HasWord(csv, "SSE", ',')) features->sse = true;
+      if (CpuFeatures_StringView_HasWord(csv, "SSE2", ','))
+        features->sse2 = true;
+      if (CpuFeatures_StringView_HasWord(csv, "SSE3", ','))
+        features->sse3 = true;
+      if (CpuFeatures_StringView_HasWord(csv, "SSSE3", ','))
+        features->ssse3 = true;
+      if (CpuFeatures_StringView_HasWord(csv, "SSE4.1", ','))
+        features->sse4_1 = true;
+      if (CpuFeatures_StringView_HasWord(csv, "SSE4.2", ','))
+        features->sse4_2 = true;
+    }
+    CpuFeatures_CloseFile(fd);
+  }
+}
+
+#endif  // CPU_FEATURES_OS_FREEBSD
+#endif  // CPU_FEATURES_ARCH_X86
diff --git a/src/impl_x86_linux_or_android.c b/src/impl_x86_linux_or_android.c
new file mode 100644
index 0000000..a4d07f3
--- /dev/null
+++ b/src/impl_x86_linux_or_android.c
@@ -0,0 +1,58 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_X86
+#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+
+#include "impl_x86__base_implementation.inl"
+
+static void OverrideOsPreserves(OsPreserves* os_preserves) {
+  (void)os_preserves;
+  // No override
+}
+
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+static void DetectFeaturesFromOs(X86Info* info, X86Features* features) {
+  (void)info;
+  // Handling Linux platform through /proc/cpuinfo.
+  const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+  if (fd >= 0) {
+    StackLineReader reader;
+    StackLineReader_Initialize(&reader, fd);
+    for (bool stop = false; !stop;) {
+      const LineResult result = StackLineReader_NextLine(&reader);
+      if (result.eof) stop = true;
+      const StringView line = result.line;
+      StringView key, value;
+      if (!CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value))
+        continue;
+      if (!CpuFeatures_StringView_IsEquals(key, str("flags"))) continue;
+      features->sse = CpuFeatures_StringView_HasWord(value, "sse", ' ');
+      features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2", ' ');
+      features->sse3 = CpuFeatures_StringView_HasWord(value, "pni", ' ');
+      features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3", ' ');
+      features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1", ' ');
+      features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2", ' ');
+      break;
+    }
+    CpuFeatures_CloseFile(fd);
+  }
+}
+
+#endif  // defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
+#endif  // CPU_FEATURES_ARCH_X86
diff --git a/src/impl_x86_macos.c b/src/impl_x86_macos.c
new file mode 100644
index 0000000..e5a5c35
--- /dev/null
+++ b/src/impl_x86_macos.c
@@ -0,0 +1,57 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_X86
+#ifdef CPU_FEATURES_OS_MACOS
+
+#include "impl_x86__base_implementation.inl"
+
+#if !defined(HAVE_SYSCTLBYNAME)
+#error "Darwin needs support for sysctlbyname"
+#endif
+#include <sys/sysctl.h>
+
+#if defined(CPU_FEATURES_MOCK_CPUID_X86)
+extern bool GetDarwinSysCtlByName(const char*);
+#else  // CPU_FEATURES_MOCK_CPUID_X86
+static bool GetDarwinSysCtlByName(const char* name) {
+  int enabled;
+  size_t enabled_len = sizeof(enabled);
+  const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0);
+  return failure ? false : enabled;
+}
+#endif
+
+static void OverrideOsPreserves(OsPreserves* os_preserves) {
+  // On Darwin AVX512 support is On-demand.
+  // We have to query the OS instead of querying the Zmm save/restore state.
+  // https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/osfmk/i386/fpu.c#L173-L199
+  os_preserves->avx512_registers = GetDarwinSysCtlByName("hw.optional.avx512f");
+}
+
+static void DetectFeaturesFromOs(X86Info* info, X86Features* features) {
+  (void)info;
+  // Handling Darwin platform through sysctlbyname.
+  features->sse = GetDarwinSysCtlByName("hw.optional.sse");
+  features->sse2 = GetDarwinSysCtlByName("hw.optional.sse2");
+  features->sse3 = GetDarwinSysCtlByName("hw.optional.sse3");
+  features->ssse3 = GetDarwinSysCtlByName("hw.optional.supplementalsse3");
+  features->sse4_1 = GetDarwinSysCtlByName("hw.optional.sse4_1");
+  features->sse4_2 = GetDarwinSysCtlByName("hw.optional.sse4_2");
+}
+
+#endif  // CPU_FEATURES_OS_MACOS
+#endif  // CPU_FEATURES_ARCH_X86
diff --git a/src/impl_x86_windows.c b/src/impl_x86_windows.c
new file mode 100644
index 0000000..0b330d0
--- /dev/null
+++ b/src/impl_x86_windows.c
@@ -0,0 +1,58 @@
+// Copyright 2017 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cpu_features_macros.h"
+
+#ifdef CPU_FEATURES_ARCH_X86
+#ifdef CPU_FEATURES_OS_WINDOWS
+
+#include "impl_x86__base_implementation.inl"
+
+static void OverrideOsPreserves(OsPreserves* os_preserves) {
+  (void)os_preserves;
+  // No override
+}
+
+#include <windows.h>  // IsProcessorFeaturePresent
+
+#if defined(CPU_FEATURES_MOCK_CPUID_X86)
+extern bool GetWindowsIsProcessorFeaturePresent(DWORD);
+#else  // CPU_FEATURES_MOCK_CPUID_X86
+static bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+  return IsProcessorFeaturePresent(ProcessorFeature);
+}
+#endif
+
+static void DetectFeaturesFromOs(X86Info* info, X86Features* features) {
+  // Handling Windows platform through IsProcessorFeaturePresent.
+  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
+  features->sse =
+      GetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+  features->sse2 =
+      GetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+  features->sse3 =
+      GetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+
+// https://github.com/google/cpu_features/issues/200
+#if (_WIN32_WINNT >= 0x0601)  // Win7+
+  if (GetX86Microarchitecture(info) == INTEL_WSM) {
+    features->ssse3 = true;
+    features->sse4_1 = true;
+    features->sse4_2 = true;
+  }
+#endif
+}
+
+#endif  // CPU_FEATURES_OS_WINDOWS
+#endif  // CPU_FEATURES_ARCH_X86
diff --git a/src/string_view.c b/src/string_view.c
index dc3158f..2cac9da 100644
--- a/src/string_view.c
+++ b/src/string_view.c
@@ -16,11 +16,20 @@
 
 #include <assert.h>
 #include <ctype.h>
-#include <string.h>
+
+#include "copy.inl"
+#include "equals.inl"
+
+static const char* CpuFeatures_memchr(const char* const ptr, const size_t size,
+                                      const char c) {
+  for (size_t i = 0; ptr && ptr[i] != '\0' && i < size; ++i)
+    if (ptr[i] == c) return ptr + i;
+  return NULL;
+}
 
 int CpuFeatures_StringView_IndexOfChar(const StringView view, char c) {
   if (view.ptr && view.size) {
-    const char* const found = (const char*)memchr(view.ptr, c, view.size);
+    const char* const found = CpuFeatures_memchr(view.ptr, view.size, c);
     if (found) {
       return (int)(found - view.ptr);
     }
@@ -48,14 +57,14 @@
 
 bool CpuFeatures_StringView_IsEquals(const StringView a, const StringView b) {
   if (a.size == b.size) {
-    return a.ptr == b.ptr || memcmp(a.ptr, b.ptr, b.size) == 0;
+    return a.ptr == b.ptr || equals(a.ptr, b.ptr, b.size);
   }
   return false;
 }
 
 bool CpuFeatures_StringView_StartsWith(const StringView a, const StringView b) {
   return a.ptr && b.ptr && b.size && a.size >= b.size
-             ? memcmp(a.ptr, b.ptr, b.size) == 0
+             ? equals(a.ptr, b.ptr, b.size)
              : false;
 }
 
@@ -138,13 +147,14 @@
     const size_t max_copy_size = dst_size - 1;
     const size_t copy_size =
         src.size > max_copy_size ? max_copy_size : src.size;
-    memcpy(dst, src.ptr, copy_size);
+    copy(dst, src.ptr, copy_size);
     dst[copy_size] = '\0';
   }
 }
 
 bool CpuFeatures_StringView_HasWord(const StringView line,
-                                    const char* const word_str) {
+                                    const char* const word_str,
+                                    const char separator) {
   const StringView word = str(word_str);
   StringView remainder = line;
   for (;;) {
@@ -157,9 +167,9 @@
       const StringView after =
           CpuFeatures_StringView_PopFront(line, index_of_word + word.size);
       const bool valid_before =
-          before.size == 0 || CpuFeatures_StringView_Back(before) == ' ';
+          before.size == 0 || CpuFeatures_StringView_Back(before) == separator;
       const bool valid_after =
-          after.size == 0 || CpuFeatures_StringView_Front(after) == ' ';
+          after.size == 0 || CpuFeatures_StringView_Front(after) == separator;
       if (valid_before && valid_after) return true;
       remainder =
           CpuFeatures_StringView_PopFront(remainder, index_of_word + word.size);
diff --git a/src/utils/list_cpu_features.c b/src/utils/list_cpu_features.c
index c80ffc5..0b4eb7a 100644
--- a/src/utils/list_cpu_features.c
+++ b/src/utils/list_cpu_features.c
@@ -340,6 +340,7 @@
     case CPU_FEATURE_CACHE_PREFETCH:
       return CreateConstantString("prefetch");
   }
+  CPU_FEATURES_UNREACHABLE();
 }
 
 static void AddCacheInfo(Node* root, const CacheInfo* cache_info) {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index c10e617..8e8f72a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -47,7 +47,13 @@
 ##------------------------------------------------------------------------------
 ## cpuinfo_x86_test
 if(PROCESSOR_IS_X86)
-  add_executable(cpuinfo_x86_test cpuinfo_x86_test.cc ../src/cpuinfo_x86.c)
+  add_executable(cpuinfo_x86_test
+    cpuinfo_x86_test.cc
+    ../src/impl_x86_freebsd.c
+    ../src/impl_x86_linux_or_android.c
+    ../src/impl_x86_macos.c
+    ../src/impl_x86_windows.c
+  )
   target_compile_definitions(cpuinfo_x86_test PUBLIC CPU_FEATURES_MOCK_CPUID_X86)
   if(APPLE)
     target_compile_definitions(cpuinfo_x86_test PRIVATE HAVE_SYSCTLBYNAME)
@@ -58,28 +64,28 @@
 ##------------------------------------------------------------------------------
 ## cpuinfo_arm_test
 if(PROCESSOR_IS_ARM)
-  add_executable(cpuinfo_arm_test cpuinfo_arm_test.cc ../src/cpuinfo_arm.c)
+  add_executable(cpuinfo_arm_test cpuinfo_arm_test.cc ../src/impl_arm_linux_or_android.c)
   target_link_libraries(cpuinfo_arm_test all_libraries)
   add_test(NAME cpuinfo_arm_test COMMAND cpuinfo_arm_test)
 endif()
 ##------------------------------------------------------------------------------
 ## cpuinfo_aarch64_test
 if(PROCESSOR_IS_AARCH64)
-  add_executable(cpuinfo_aarch64_test cpuinfo_aarch64_test.cc ../src/cpuinfo_aarch64.c)
+  add_executable(cpuinfo_aarch64_test cpuinfo_aarch64_test.cc ../src/impl_aarch64_linux_or_android.c)
   target_link_libraries(cpuinfo_aarch64_test all_libraries)
   add_test(NAME cpuinfo_aarch64_test COMMAND cpuinfo_aarch64_test)
 endif()
 ##------------------------------------------------------------------------------
 ## cpuinfo_mips_test
 if(PROCESSOR_IS_MIPS)
-  add_executable(cpuinfo_mips_test cpuinfo_mips_test.cc  ../src/cpuinfo_mips.c)
+  add_executable(cpuinfo_mips_test cpuinfo_mips_test.cc  ../src/impl_mips_linux_or_android.c)
   target_link_libraries(cpuinfo_mips_test all_libraries)
   add_test(NAME cpuinfo_mips_test COMMAND cpuinfo_mips_test)
 endif()
 ##------------------------------------------------------------------------------
 ## cpuinfo_ppc_test
 if(PROCESSOR_IS_POWER)
-  add_executable(cpuinfo_ppc_test cpuinfo_ppc_test.cc  ../src/cpuinfo_ppc.c)
+  add_executable(cpuinfo_ppc_test cpuinfo_ppc_test.cc  ../src/impl_ppc_linux.c)
   target_link_libraries(cpuinfo_ppc_test all_libraries)
   add_test(NAME cpuinfo_ppc_test COMMAND cpuinfo_ppc_test)
 endif()
diff --git a/test/cpuinfo_aarch64_test.cc b/test/cpuinfo_aarch64_test.cc
index 5afaaa8..04b6143 100644
--- a/test/cpuinfo_aarch64_test.cc
+++ b/test/cpuinfo_aarch64_test.cc
@@ -24,6 +24,7 @@
 void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
 
 TEST(CpuinfoAarch64Test, FromHardwareCap) {
+  ResetHwcaps();
   SetHardwareCapabilities(AARCH64_HWCAP_FP | AARCH64_HWCAP_AES, 0);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
   const auto info = GetAarch64Info();
@@ -62,6 +63,7 @@
 }
 
 TEST(CpuinfoAarch64Test, FromHardwareCap2) {
+  ResetHwcaps();
   SetHardwareCapabilities(AARCH64_HWCAP_FP,
                           AARCH64_HWCAP2_SVE2 | AARCH64_HWCAP2_BTI);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
@@ -90,7 +92,7 @@
 }
 
 TEST(CpuinfoAarch64Test, ARMCortexA53) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(Processor   : AArch64 Processor rev 3 (aarch64)
@@ -165,6 +167,7 @@
   EXPECT_FALSE(info.features.dgh);
   EXPECT_FALSE(info.features.rng);
   EXPECT_FALSE(info.features.bti);
+  EXPECT_FALSE(info.features.mte);
 }
 
 }  // namespace
diff --git a/test/cpuinfo_arm_test.cc b/test/cpuinfo_arm_test.cc
index e0b08a4..ad7f4e8 100644
--- a/test/cpuinfo_arm_test.cc
+++ b/test/cpuinfo_arm_test.cc
@@ -21,9 +21,8 @@
 namespace cpu_features {
 namespace {
 
-void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
-
 TEST(CpuinfoArmTest, FromHardwareCap) {
+  ResetHwcaps();
   SetHardwareCapabilities(ARM_HWCAP_NEON, ARM_HWCAP2_AES | ARM_HWCAP2_CRC32);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
   const auto info = GetArmInfo();
@@ -52,7 +51,7 @@
 }
 
 TEST(CpuinfoArmTest, ODroidFromCpuInfo) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(processor       : 0
 model name      : ARMv7 Processor rev 3 (v71)
@@ -101,7 +100,7 @@
 
 // Linux test-case
 TEST(CpuinfoArmTest, RaspberryPiZeroFromCpuInfo) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(processor       : 0
 model name      : ARMv6-compatible processor rev 7 (v6l)
@@ -153,7 +152,7 @@
 }
 
 TEST(CpuinfoArmTest, MarvellArmadaFromCpuInfo) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(processor       : 0
 model name      : ARMv7 Processor rev 1 (v7l)
@@ -217,7 +216,7 @@
 // Android test-case
 // http://code.google.com/p/android/issues/detail?id=10812
 TEST(CpuinfoArmTest, InvalidArmv7) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(Processor       : ARMv6-compatible processor rev 6 (v6l)
@@ -267,6 +266,7 @@
 // Android test-case
 // https://crbug.com/341598.
 TEST(CpuinfoArmTest, InvalidNeon) {
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(Processor: ARMv7 Processory rev 0 (v71)
@@ -294,7 +294,7 @@
 // The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
 // support.
 TEST(CpuinfoArmTest, Nexus4_0x510006f2) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(CPU implementer	: 0x51
@@ -312,7 +312,7 @@
 // The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
 // support.
 TEST(CpuinfoArmTest, Nexus4_0x510006f3) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(CPU implementer	: 0x51
@@ -327,11 +327,29 @@
   EXPECT_EQ(GetArmCpuId(&info), 0x510006f3);
 }
 
+// The 2013 Nexus 7 (Qualcomm Krait) kernel configuration forgets to report IDIV
+// support.
+TEST(CpuinfoArmTest, Nexus7_2013_0x511006f0) {
+  ResetHwcaps();
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/proc/cpuinfo",
+                R"(CPU implementer  : 0x51
+CPU architecture: 7
+CPU variant : 0x1
+CPU part  : 0x06f
+CPU revision  : 0)");
+  const auto info = GetArmInfo();
+  EXPECT_TRUE(info.features.idiva);
+  EXPECT_TRUE(info.features.idivt);
+
+  EXPECT_EQ(GetArmCpuId(&info), 0x511006f0);
+}
+
 // The emulator-specific Android 4.2 kernel fails to report support for the
 // 32-bit ARM IDIV instruction. Technically, this is a feature of the virtual
 // CPU implemented by the emulator.
 TEST(CpuinfoArmTest, EmulatorSpecificIdiv) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(Processor	: ARMv7 Processor rev 0 (v7l)
diff --git a/test/cpuinfo_mips_test.cc b/test/cpuinfo_mips_test.cc
index d734058..a01624a 100644
--- a/test/cpuinfo_mips_test.cc
+++ b/test/cpuinfo_mips_test.cc
@@ -24,9 +24,8 @@
 
 namespace {
 
-void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
-
 TEST(CpuinfoMipsTest, FromHardwareCapBoth) {
+  ResetHwcaps();
   SetHardwareCapabilities(MIPS_HWCAP_MSA | MIPS_HWCAP_R6, 0);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
   const auto info = GetMipsInfo();
@@ -36,6 +35,7 @@
 }
 
 TEST(CpuinfoMipsTest, FromHardwareCapOnlyOne) {
+  ResetHwcaps();
   SetHardwareCapabilities(MIPS_HWCAP_MSA, 0);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
   const auto info = GetMipsInfo();
@@ -44,7 +44,7 @@
 }
 
 TEST(CpuinfoMipsTest, Ci40) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(system type : IMG Pistachio SoC (B0)
 machine : IMG Marduk – Ci40 with cc2520
@@ -72,7 +72,7 @@
 }
 
 TEST(CpuinfoMipsTest, AR7161) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(system type             : Atheros AR7161 rev 2
@@ -98,7 +98,7 @@
 }
 
 TEST(CpuinfoMipsTest, Goldfish) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(system type		: MIPS-Goldfish
 Hardware		: goldfish
diff --git a/test/cpuinfo_ppc_test.cc b/test/cpuinfo_ppc_test.cc
index 8f0cb65..b43a7c8 100644
--- a/test/cpuinfo_ppc_test.cc
+++ b/test/cpuinfo_ppc_test.cc
@@ -22,9 +22,8 @@
 namespace cpu_features {
 namespace {
 
-void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
-
 TEST(CpustringsPPCTest, FromHardwareCap) {
+  ResetHwcaps();
   SetHardwareCapabilities(PPC_FEATURE_HAS_FPU | PPC_FEATURE_HAS_VSX,
                           PPC_FEATURE2_ARCH_3_00);
   GetEmptyFilesystem();  // disabling /proc/cpuinfo
@@ -40,7 +39,7 @@
 }
 
 TEST(CpustringsPPCTest, Blade) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(processor       : 14
@@ -57,7 +56,8 @@
 platform        : pSeries
 model           : IBM,8406-70Y
 machine         : CHRP IBM,8406-70Y)");
-  SetPlatformTypes("power7", "power8");
+  SetPlatformPointer("power7");
+  SetBasePlatformPointer("power8");
   const auto strings = GetPPCPlatformStrings();
   ASSERT_STREQ(strings.platform, "pSeries");
   ASSERT_STREQ(strings.model, "IBM,8406-70Y");
@@ -68,7 +68,7 @@
 }
 
 TEST(CpustringsPPCTest, Firestone) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(processor       : 126
@@ -94,7 +94,7 @@
 }
 
 TEST(CpustringsPPCTest, w8) {
-  DisableHardwareCapabilities();
+  ResetHwcaps();
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo",
                 R"(processor       : 143
diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc
index 636d0f9..56243b9 100644
--- a/test/cpuinfo_x86_test.cc
+++ b/test/cpuinfo_x86_test.cc
@@ -48,7 +48,7 @@
     xcr0_eax_ = os_backups_extended_registers ? -1 : 0;
   }
 
-#if defined(CPU_FEATURES_OS_DARWIN)
+#if defined(CPU_FEATURES_OS_MACOS)
   bool GetDarwinSysCtlByName(std::string name) const {
     return darwin_sysctlbyname_.count(name);
   }
@@ -56,7 +56,7 @@
   void SetDarwinSysCtlByName(std::string name) {
     darwin_sysctlbyname_.insert(name);
   }
-#endif  // CPU_FEATURES_OS_DARWIN
+#endif  // CPU_FEATURES_OS_MACOS
 
 #if defined(CPU_FEATURES_OS_WINDOWS)
   bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
@@ -70,32 +70,37 @@
 
  private:
   std::map<std::pair<uint32_t, int>, Leaf> cpuid_leaves_;
-#if defined(CPU_FEATURES_OS_DARWIN)
+#if defined(CPU_FEATURES_OS_MACOS)
   std::set<std::string> darwin_sysctlbyname_;
-#endif  // CPU_FEATURES_OS_DARWIN
+#endif  // CPU_FEATURES_OS_MACOS
 #if defined(CPU_FEATURES_OS_WINDOWS)
   std::set<DWORD> windows_isprocessorfeaturepresent_;
 #endif  // CPU_FEATURES_OS_WINDOWS
   uint32_t xcr0_eax_;
 };
 
-FakeCpu* g_fake_cpu = nullptr;
+static FakeCpu* g_fake_cpu_instance = nullptr;
+
+static FakeCpu& cpu() {
+  assert(g_fake_cpu_instance != nullptr);
+  return *g_fake_cpu_instance;
+}
 
 extern "C" Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
-  return g_fake_cpu->GetCpuidLeaf(leaf_id, ecx);
+  return cpu().GetCpuidLeaf(leaf_id, ecx);
 }
 
-extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
+extern "C" uint32_t GetXCR0Eax(void) { return cpu().GetXCR0Eax(); }
 
-#if defined(CPU_FEATURES_OS_DARWIN)
+#if defined(CPU_FEATURES_OS_MACOS)
 extern "C" bool GetDarwinSysCtlByName(const char* name) {
-  return g_fake_cpu->GetDarwinSysCtlByName(name);
+  return cpu().GetDarwinSysCtlByName(name);
 }
-#endif  // CPU_FEATURES_OS_DARWIN
+#endif  // CPU_FEATURES_OS_MACOS
 
 #if defined(CPU_FEATURES_OS_WINDOWS)
 extern "C" bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
-  return g_fake_cpu->GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
+  return cpu().GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
 }
 #endif  // CPU_FEATURES_OS_WINDOWS
 
@@ -103,13 +108,19 @@
 
 class CpuidX86Test : public ::testing::Test {
  protected:
-  void SetUp() override { g_fake_cpu = new FakeCpu(); }
-  void TearDown() override { delete g_fake_cpu; }
+  void SetUp() override {
+    assert(g_fake_cpu_instance == nullptr);
+    g_fake_cpu_instance = new FakeCpu();
+  }
+  void TearDown() override {
+    delete g_fake_cpu_instance;
+    g_fake_cpu_instance = nullptr;
+  }
 };
 
 TEST_F(CpuidX86Test, SandyBridge) {
-  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
-  g_fake_cpu->SetLeaves({
+  cpu().SetOsBackupsExtendedRegisters(true);
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
       {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@@ -148,28 +159,30 @@
   EXPECT_TRUE(features.popcnt);
   EXPECT_FALSE(features.movbe);
   EXPECT_FALSE(features.rdrnd);
+  EXPECT_FALSE(features.adx);
 }
 
+const int UNDEF = -1;
 const int KiB = 1024;
 const int MiB = 1024 * KiB;
 
 TEST_F(CpuidX86Test, SandyBridgeTestOsSupport) {
-  g_fake_cpu->SetLeaves({
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
       {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
   });
   // avx is disabled if os does not support backing up ymm registers.
-  g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+  cpu().SetOsBackupsExtendedRegisters(false);
   EXPECT_FALSE(GetX86Info().features.avx);
   // avx is disabled if os does not support backing up ymm registers.
-  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+  cpu().SetOsBackupsExtendedRegisters(true);
   EXPECT_TRUE(GetX86Info().features.avx);
 }
 
 TEST_F(CpuidX86Test, SkyLake) {
-  g_fake_cpu->SetOsBackupsExtendedRegisters(true);
-  g_fake_cpu->SetLeaves({
+  cpu().SetOsBackupsExtendedRegisters(true);
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
       {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
@@ -183,7 +196,7 @@
 }
 
 TEST_F(CpuidX86Test, Branding) {
-  g_fake_cpu->SetLeaves({
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
       {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
@@ -199,7 +212,7 @@
 }
 
 TEST_F(CpuidX86Test, KabyLakeCache) {
-  g_fake_cpu->SetLeaves({
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
       {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
@@ -248,7 +261,7 @@
 }
 
 TEST_F(CpuidX86Test, HSWCache) {
-  g_fake_cpu->SetLeaves({
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
       {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
@@ -296,9 +309,226 @@
   EXPECT_EQ(info.levels[3].partitioning, 1);
 }
 
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0200F30_K11_Griffin_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K11_GRIFFIN) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000001, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00200F30, 0x00020800, 0x00002001, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001A, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00200F30, 0x20000000, 0x0000131F, 0xEBD3FBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x11);
+  EXPECT_EQ(info.model, 0x03);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_K11);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0300F10_K12_Llano_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K12_LLANO) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00300F10, 0x00040800, 0x00802009, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00300F10, 0x20002B31, 0x000037FF, 0xEFD3FBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x12);
+  EXPECT_EQ(info.model, 0x01);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_K12);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F01_K14_Bobcat_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F01) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00500F01, 0x00020800, 0x00802209, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00500F01, 0x00000000, 0x000035FF, 0x2FD3FBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x14);
+  EXPECT_EQ(info.model, 0x00);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_BOBCAT);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F10_K14_Bobcat_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F10) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00500F10, 0x00020800, 0x00802209, 0x178BFBFF}},
+      {{0x00000002, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+      {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+      {{0x00000005, 0}, Leaf{0x00000040, 0x00000040, 0x00000003, 0x00000000}},
+      {{0x00000006, 0}, Leaf{0x00000000, 0x00000000, 0x00000001, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00500F10, 0x00001242, 0x000035FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x35332D45, 0x72502030, 0x7365636F}},
+      {{0x80000003, 0}, Leaf{0x00726F73, 0x00000000, 0x00000000, 0x00000000}},
+      {{0x80000004, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+      {{0x80000005, 0}, Leaf{0xFF08FF08, 0xFF280000, 0x20080140, 0x20020140}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x14);
+  EXPECT_EQ(info.model, 0x01);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_BOBCAT);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0500F20_K14_Bobcat_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K14_BOBCAT_AMD0500F20) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00500F20, 0x00020800, 0x00802209, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00500F20, 0x000012E9, 0x000035FF, 0x2FD3FBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x14);
+  EXPECT_EQ(info.model, 0x02);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_BOBCAT);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0670F00_K15_StoneyRidge_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K15_EXCAVATOR_STONEY_RIDGE) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00670F00, 0x00020800, 0x7ED8320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x000001A9, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00670F00, 0x00000000, 0x2FABBFFF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x392D3941, 0x20303134, 0x45444152}},
+      {{0x80000003, 0}, Leaf{0x52204E4F, 0x35202C35, 0x4D4F4320, 0x45545550}},
+      {{0x80000004, 0}, Leaf{0x524F4320, 0x32205345, 0x47332B43, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x15);
+  EXPECT_EQ(info.model, 0x70);
+  EXPECT_STREQ(info.brand_string,
+               "AMD A9-9410 RADEON R5, 5 COMPUTE CORES 2C+3G   ");
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::AMD_EXCAVATOR);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD A9-9410 RADEON R5, 5 COMPUTE CORES 2C+3G   ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F20_K15_AbuDhabi_CPUID0.txt
+TEST_F(CpuidX86Test, AMD_K15_PILEDRIVER_ABU_DHABI) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00600F20, 0x00100800, 0x3E98320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00600F20, 0x30000000, 0x01EBBFFF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x6574704F, 0x286E6F72, 0x20296D74}},
+      {{0x80000003, 0}, Leaf{0x636F7250, 0x6F737365, 0x33362072, 0x20203637}},
+      {{0x80000004, 0}, Leaf{0x20202020, 0x20202020, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x15);
+  EXPECT_EQ(info.model, 0x02);
+  EXPECT_STREQ(info.brand_string,
+               "AMD Opteron(tm) Processor 6376                 ");
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::AMD_PILEDRIVER);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD Opteron(tm) Processor 6376                 ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F20_K15_AbuDhabi_CPUID0.txt
+TEST_F(CpuidX86Test, AMD_K15_PILEDRIVER_ABU_DHABI_CACHE_INFO) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00600F20, 0x00100800, 0x3E98320B, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00600F20, 0x30000000, 0x01EBBFFF, 0x2FD3FBFF}},
+      {{0x8000001D, 0}, Leaf{0x00000121, 0x00C0003F, 0x0000003F, 0x00000000}},
+      {{0x8000001D, 1}, Leaf{0x00004122, 0x0040003F, 0x000001FF, 0x00000000}},
+      {{0x8000001D, 2}, Leaf{0x00004143, 0x03C0003F, 0x000007FF, 0x00000001}},
+      {{0x8000001D, 3}, Leaf{0x0001C163, 0x0BC0003F, 0x000007FF, 0x00000001}},
+  });
+  const auto info = GetX86CacheInfo();
+
+  EXPECT_EQ(info.size, 4);
+  EXPECT_EQ(info.levels[0].level, 1);
+  EXPECT_EQ(info.levels[0].cache_type, 1);
+  EXPECT_EQ(info.levels[0].cache_size, 16 * KiB);
+  EXPECT_EQ(info.levels[0].ways, 4);
+  EXPECT_EQ(info.levels[0].line_size, 64);
+  EXPECT_EQ(info.levels[0].tlb_entries, 64);
+  EXPECT_EQ(info.levels[0].partitioning, 1);
+
+  EXPECT_EQ(info.levels[1].level, 1);
+  EXPECT_EQ(info.levels[1].cache_type, 2);
+  EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
+  EXPECT_EQ(info.levels[1].ways, 2);
+  EXPECT_EQ(info.levels[1].line_size, 64);
+  EXPECT_EQ(info.levels[1].tlb_entries, 512);
+  EXPECT_EQ(info.levels[1].partitioning, 1);
+
+  EXPECT_EQ(info.levels[2].level, 2);
+  EXPECT_EQ(info.levels[2].cache_type, 3);
+  EXPECT_EQ(info.levels[2].cache_size, 2 * MiB);
+  EXPECT_EQ(info.levels[2].ways, 16);
+  EXPECT_EQ(info.levels[2].line_size, 64);
+  EXPECT_EQ(info.levels[2].tlb_entries, 2048);
+  EXPECT_EQ(info.levels[2].partitioning, 1);
+
+  EXPECT_EQ(info.levels[3].level, 3);
+  EXPECT_EQ(info.levels[3].cache_type, 3);
+  EXPECT_EQ(info.levels[3].cache_size, 6 * MiB);
+  EXPECT_EQ(info.levels[3].ways, 48);
+  EXPECT_EQ(info.levels[3].line_size, 64);
+  EXPECT_EQ(info.levels[3].tlb_entries, 2048);
+  EXPECT_EQ(info.levels[3].partitioning, 1);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0600F12_K15_Interlagos_CPUID3.txt
+TEST_F(CpuidX86Test, AMD_K15_BULLDOZER_INTERLAGOS) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00600F12, 0x000C0800, 0x1E98220B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00600F12, 0x30000000, 0x01C9BFFF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x6574704F, 0x286E6F72, 0x20294D54}},
+      {{0x80000003, 0}, Leaf{0x636F7250, 0x6F737365, 0x32362072, 0x20203833}},
+      {{0x80000004, 0}, Leaf{0x20202020, 0x20202020, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x15);
+  EXPECT_EQ(info.model, 0x01);
+  EXPECT_STREQ(info.brand_string,
+               "AMD Opteron(TM) Processor 6238                 ");
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::AMD_BULLDOZER);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD Opteron(TM) Processor 6238                 ");
+}
+
 // http://users.atw.hu/instlatx64/AuthenticAMD0630F81_K15_Godavari_CPUID.txt
-TEST_F(CpuidX86Test, AMD_K15) {
-  g_fake_cpu->SetLeaves({
+TEST_F(CpuidX86Test, AMD_K15_STREAMROLLER_GODAVARI) {
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
       {{0x00000001, 0}, Leaf{0x00630F81, 0x00040800, 0x3E98320B, 0x178BFBFF}},
       {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
@@ -315,41 +545,277 @@
   EXPECT_EQ(info.family, 0x15);
   EXPECT_EQ(info.model, 0x38);
   EXPECT_EQ(info.stepping, 0x01);
+  EXPECT_STREQ(info.brand_string,
+               "AMD A8-7670K Radeon R7, 10 Compute Cores 4C+6G ");
   EXPECT_EQ(GetX86Microarchitecture(&info),
-            X86Microarchitecture::AMD_BULLDOZER);
+            X86Microarchitecture::AMD_STREAMROLLER);
 
   char brand_string[49];
   FillX86BrandString(brand_string);
   EXPECT_STREQ(brand_string, "AMD A8-7670K Radeon R7, 10 Compute Cores 4C+6G ");
 }
 
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0700F01_K16_Kabini_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K16_JAGUAR_KABINI) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00700F01, 0x00040800, 0x3ED8220B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00700F01, 0x00000000, 0x154037FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x352D3441, 0x20303030, 0x20555041}},
+      {{0x80000003, 0}, Leaf{0x68746977, 0x64615220, 0x286E6F65, 0x20294D54}},
+      {{0x80000004, 0}, Leaf{0x47204448, 0x68706172, 0x20736369, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x16);
+  EXPECT_EQ(info.model, 0x00);
+  EXPECT_STREQ(info.brand_string,
+               "AMD A4-5000 APU with Radeon(TM) HD Graphics    ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_JAGUAR);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD A4-5000 APU with Radeon(TM) HD Graphics    ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0730F01_K16_Beema_CPUID2.txt
+TEST_F(CpuidX86Test, AMD_K16_PUMA_BEEMA) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00730F01, 0x00040800, 0x7ED8220B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x00000008, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00730F01, 0x00000000, 0x1D4037FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x362D3641, 0x20303133, 0x20555041}},
+      {{0x80000003, 0}, Leaf{0x68746977, 0x444D4120, 0x64615220, 0x206E6F65}},
+      {{0x80000004, 0}, Leaf{0x47203452, 0x68706172, 0x20736369, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x16);
+  EXPECT_EQ(info.model, 0x30);
+  EXPECT_STREQ(info.brand_string,
+               "AMD A6-6310 APU with AMD Radeon R4 Graphics    ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_PUMA);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD A6-6310 APU with AMD Radeon R4 Graphics    ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0820F01_K17_Dali_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K17_ZEN_DALI) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00820F01, 0x00020800, 0x7ED8320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x209C01A9, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001F, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00820F01, 0x00000000, 0x35C233FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x30323033, 0x69772065, 0x52206874}},
+      {{0x80000003, 0}, Leaf{0x6F656461, 0x7247206E, 0x69687061, 0x20207363}},
+      {{0x80000004, 0}, Leaf{0x20202020, 0x20202020, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x17);
+  EXPECT_EQ(info.model, 0x20);
+  EXPECT_STREQ(info.brand_string,
+               "AMD 3020e with Radeon Graphics                 ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD 3020e with Radeon Graphics                 ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0800F82_K17_ZenP_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K17_ZEN_PLUS_PINNACLE_RIDGE) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00800F82, 0x00100800, 0x7ED8320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x209C01A9, 0x00000000, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001F, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00800F82, 0x20000000, 0x35C233FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x657A7952, 0x2037206E, 0x30303732}},
+      {{0x80000003, 0}, Leaf{0x69452058, 0x2D746867, 0x65726F43, 0x6F725020}},
+      {{0x80000004, 0}, Leaf{0x73736563, 0x2020726F, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x17);
+  EXPECT_EQ(info.model, 0x08);
+  EXPECT_STREQ(info.brand_string,
+               "AMD Ryzen 7 2700X Eight-Core Processor         ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN_PLUS);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD Ryzen 7 2700X Eight-Core Processor         ");
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0840F70_K17_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K17_ZEN2_XBOX_SERIES_X) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00840F70, 0x00100800, 0x7ED8320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x219C91A9, 0x00400004, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x80000020, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00840F70, 0x00000000, 0xF5C2B7FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x30303734, 0x2D382053, 0x65726F43}},
+      {{0x80000003, 0}, Leaf{0x6F725020, 0x73736563, 0x4420726F, 0x746B7365}},
+      {{0x80000004, 0}, Leaf{0x4B20706F, 0x00007469, 0x00000000, 0x00000000}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x17);
+  EXPECT_EQ(info.model, 0x47);
+  EXPECT_STREQ(info.brand_string, "AMD 4700S 8-Core Processor Desktop Kit");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN2);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD 4700S 8-Core Processor Desktop Kit");
+}
+
+// http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F02_Hygon_CPUID3.txt
+TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x6F677948, 0x656E6975, 0x6E65476E}},
+      {{0x00000001, 0}, Leaf{0x00900F02, 0x00100800, 0x74D83209, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x009C01A9, 0x0040068C, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x8000001F, 0x6F677948, 0x656E6975, 0x6E65476E}},
+      {{0x80000001, 0}, Leaf{0x00900F02, 0x60000000, 0x35C233FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x6F677948, 0x3843206E, 0x31332036, 0x20203538}},
+      {{0x80000003, 0}, Leaf{0x6F632D38, 0x50206572, 0x65636F72, 0x726F7373}},
+      {{0x80000004, 0}, Leaf{0x20202020, 0x20202020, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "HygonGenuine");
+  EXPECT_EQ(info.family, 0x18);
+  EXPECT_EQ(info.model, 0x00);
+  EXPECT_STREQ(info.brand_string,
+               "Hygon C86 3185  8-core Processor               ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "Hygon C86 3185  8-core Processor               ");
+}
+
+// http://users.atw.hu/instlatx64/HygonGenuine/HygonGenuine0900F02_Hygon_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K18_ZEN_DHYANA_CACHE_INFO) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x6F677948, 0x656E6975, 0x6E65476E}},
+      {{0x00000001, 0}, Leaf{0x00900F02, 0x00100800, 0x74D83209, 0x178BFBFF}},
+      {{0x80000000, 0}, Leaf{0x8000001F, 0x6F677948, 0x656E6975, 0x6E65476E}},
+      {{0x80000001, 0}, Leaf{0x00900F02, 0x60000000, 0x35C233FF, 0x2FD3FBFF}},
+      {{0x8000001D, 0}, Leaf{0x00004121, 0x01C0003F, 0x0000003F, 0x00000000}},
+      {{0x8000001D, 1}, Leaf{0x00004122, 0x00C0003F, 0x000000FF, 0x00000000}},
+      {{0x8000001D, 2}, Leaf{0x00004143, 0x01C0003F, 0x000003FF, 0x00000002}},
+      {{0x8000001D, 3}, Leaf{0x0001C163, 0x03C0003F, 0x00001FFF, 0x00000001}},
+  });
+  const auto info = GetX86CacheInfo();
+
+  EXPECT_EQ(info.size, 4);
+  EXPECT_EQ(info.levels[0].level, 1);
+  EXPECT_EQ(info.levels[0].cache_type, 1);
+  EXPECT_EQ(info.levels[0].cache_size, 32 * KiB);
+  EXPECT_EQ(info.levels[0].ways, 8);
+  EXPECT_EQ(info.levels[0].line_size, 64);
+  EXPECT_EQ(info.levels[0].tlb_entries, 64);
+  EXPECT_EQ(info.levels[0].partitioning, 1);
+
+  EXPECT_EQ(info.levels[1].level, 1);
+  EXPECT_EQ(info.levels[1].cache_type, 2);
+  EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
+  EXPECT_EQ(info.levels[1].ways, 4);
+  EXPECT_EQ(info.levels[1].line_size, 64);
+  EXPECT_EQ(info.levels[1].tlb_entries, 256);
+  EXPECT_EQ(info.levels[1].partitioning, 1);
+
+  EXPECT_EQ(info.levels[2].level, 2);
+  EXPECT_EQ(info.levels[2].cache_type, 3);
+  EXPECT_EQ(info.levels[2].cache_size, 512 * KiB);
+  EXPECT_EQ(info.levels[2].ways, 8);
+  EXPECT_EQ(info.levels[2].line_size, 64);
+  EXPECT_EQ(info.levels[2].tlb_entries, 1024);
+  EXPECT_EQ(info.levels[2].partitioning, 1);
+
+  EXPECT_EQ(info.levels[3].level, 3);
+  EXPECT_EQ(info.levels[3].cache_type, 3);
+  EXPECT_EQ(info.levels[3].cache_size, 8 * MiB);
+  EXPECT_EQ(info.levels[3].ways, 16);
+  EXPECT_EQ(info.levels[3].line_size, 64);
+  EXPECT_EQ(info.levels[3].tlb_entries, 8192);
+  EXPECT_EQ(info.levels[3].partitioning, 1);
+}
+
+// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0A20F10_K19_Vermeer2_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K19_ZEN3_VERMEER) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000010, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x00000001, 0}, Leaf{0x00A20F10, 0x01180800, 0x7ED8320B, 0x178BFBFF}},
+      {{0x00000007, 0}, Leaf{0x00000000, 0x219C97A9, 0x0040068C, 0x00000000}},
+      {{0x80000000, 0}, Leaf{0x80000023, 0x68747541, 0x444D4163, 0x69746E65}},
+      {{0x80000001, 0}, Leaf{0x00A20F10, 0x20000000, 0x75C237FF, 0x2FD3FBFF}},
+      {{0x80000002, 0}, Leaf{0x20444D41, 0x657A7952, 0x2039206E, 0x30303935}},
+      {{0x80000003, 0}, Leaf{0x32312058, 0x726F432D, 0x72502065, 0x7365636F}},
+      {{0x80000004, 0}, Leaf{0x20726F73, 0x20202020, 0x20202020, 0x00202020}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "AuthenticAMD");
+  EXPECT_EQ(info.family, 0x19);
+  EXPECT_EQ(info.model, 0x21);
+  EXPECT_STREQ(info.brand_string,
+               "AMD Ryzen 9 5900X 12-Core Processor            ");
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::AMD_ZEN3);
+
+  char brand_string[49];
+  FillX86BrandString(brand_string);
+  EXPECT_STREQ(brand_string, "AMD Ryzen 9 5900X 12-Core Processor            ");
+}
+
 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel00106A1_Nehalem_CPUID.txt
 TEST_F(CpuidX86Test, Nehalem) {
   // Pre AVX cpus don't have xsave
-  g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+  cpu().SetOsBackupsExtendedRegisters(false);
 #if defined(CPU_FEATURES_OS_WINDOWS)
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_XMMI_INSTRUCTIONS_AVAILABLE);
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_XMMI64_INSTRUCTIONS_AVAILABLE);
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_SSE3_INSTRUCTIONS_AVAILABLE);
-#endif  // CPU_FEATURES_OS_WINDOWS
-#if defined(CPU_FEATURES_OS_DARWIN)
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
-#endif  // CPU_FEATURES_OS_DARWIN
-#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+#elif defined(CPU_FEATURES_OS_MACOS)
+  cpu().SetDarwinSysCtlByName("hw.optional.sse");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse2");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse3");
+  cpu().SetDarwinSysCtlByName("hw.optional.supplementalsse3");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse4_1");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse4_2");
+#elif defined(CPU_FEATURES_OS_FREEBSD)
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/var/run/dmesg.boot", R"(
+  ---<<BOOT>>---
+Copyright (c) 1992-2020 The FreeBSD Project.
+FreeBSD is a registered trademark of The FreeBSD Foundation.
+  Features=0x1783fbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE,SSE2,HTT>
+  Features2=0x5eda2203<SSE3,PCLMULQDQ,SSSE3,CX16,PCID,SSE4.1,SSE4.2,MOVBE,POPCNT,AESNI,XSAVE,OSXSAVE,RDRAND>
+real memory  = 2147418112 (2047 MB)
+)");
+#elif defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(processor       :
-flags           : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+flags           : fpu mmx sse sse2 pni ssse3 sse4_1 sse4_2
 )");
-#endif  // CPU_FEATURES_OS_LINUX_OR_ANDROID
-  g_fake_cpu->SetLeaves({
+#endif
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x000106A2, 0x00100800, 0x00BCE3BD, 0xBFEBFBFF}},
       {{0x00000002, 0}, Leaf{0x55035A01, 0x00F0B0E3, 0x00000000, 0x09CA212C}},
@@ -382,6 +848,8 @@
   EXPECT_EQ(info.family, 0x06);
   EXPECT_EQ(info.model, 0x1A);
   EXPECT_EQ(info.stepping, 0x02);
+  EXPECT_STREQ(info.brand_string,
+               "Genuine Intel(R) CPU           @ 0000 @ 1.87GHz");
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_NHM);
 
   char brand_string[49];
@@ -391,42 +859,47 @@
   EXPECT_TRUE(info.features.sse);
   EXPECT_TRUE(info.features.sse2);
   EXPECT_TRUE(info.features.sse3);
-#ifndef CPU_FEATURES_OS_WINDOWS
+#if !defined(CPU_FEATURES_OS_WINDOWS)
   // Currently disabled on Windows as IsProcessorFeaturePresent do not support
   // feature detection > sse3.
   EXPECT_TRUE(info.features.ssse3);
   EXPECT_TRUE(info.features.sse4_1);
   EXPECT_TRUE(info.features.sse4_2);
-#endif  // CPU_FEATURES_OS_WINDOWS
+#endif  // !defined(CPU_FEATURES_OS_WINDOWS)
 }
 
 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0030673_Silvermont3_CPUID.txt
 TEST_F(CpuidX86Test, Atom) {
   // Pre AVX cpus don't have xsave
-  g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+  cpu().SetOsBackupsExtendedRegisters(false);
 #if defined(CPU_FEATURES_OS_WINDOWS)
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_XMMI_INSTRUCTIONS_AVAILABLE);
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_XMMI64_INSTRUCTIONS_AVAILABLE);
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_SSE3_INSTRUCTIONS_AVAILABLE);
-#endif  // CPU_FEATURES_OS_WINDOWS
-#if defined(CPU_FEATURES_OS_DARWIN)
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
-#endif  // CPU_FEATURES_OS_DARWIN
-#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+#elif defined(CPU_FEATURES_OS_MACOS)
+  cpu().SetDarwinSysCtlByName("hw.optional.sse");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse2");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse3");
+  cpu().SetDarwinSysCtlByName("hw.optional.supplementalsse3");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse4_1");
+  cpu().SetDarwinSysCtlByName("hw.optional.sse4_2");
+#elif defined(CPU_FEATURES_OS_FREEBSD)
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/var/run/dmesg.boot", R"(
+  ---<<BOOT>>---
+Copyright (c) 1992-2020 The FreeBSD Project.
+FreeBSD is a registered trademark of The FreeBSD Foundation.
+  Features=0x1783fbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE,SSE2,HTT>
+  Features2=0x5eda2203<SSE3,PCLMULQDQ,SSSE3,CX16,PCID,SSE4.1,SSE4.2,MOVBE,POPCNT,AESNI,XSAVE,OSXSAVE,RDRAND>
+real memory  = 2147418112 (2047 MB)
+)");
+#elif defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(
-flags           : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+flags           : fpu mmx sse sse2 pni ssse3 sse4_1 sse4_2
 )");
-#endif  // CPU_FEATURES_OS_LINUX_OR_ANDROID
-  g_fake_cpu->SetLeaves({
+#endif
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x00030673, 0x00100800, 0x41D8E3BF, 0xBFEBFBFF}},
       {{0x00000002, 0}, Leaf{0x61B3A001, 0x0000FFC2, 0x00000000, 0x00000000}},
@@ -458,6 +931,8 @@
   EXPECT_EQ(info.family, 0x06);
   EXPECT_EQ(info.model, 0x37);
   EXPECT_EQ(info.stepping, 0x03);
+  EXPECT_STREQ(info.brand_string,
+               "      Intel(R) Celeron(R) CPU  J1900  @ 1.99GHz");
   EXPECT_EQ(GetX86Microarchitecture(&info),
             X86Microarchitecture::INTEL_ATOM_SMT);
 
@@ -468,33 +943,91 @@
   EXPECT_TRUE(info.features.sse);
   EXPECT_TRUE(info.features.sse2);
   EXPECT_TRUE(info.features.sse3);
-#ifndef CPU_FEATURES_OS_WINDOWS
+#if !defined(CPU_FEATURES_OS_WINDOWS)
   // Currently disabled on Windows as IsProcessorFeaturePresent do not support
   // feature detection > sse3.
   EXPECT_TRUE(info.features.ssse3);
   EXPECT_TRUE(info.features.sse4_1);
   EXPECT_TRUE(info.features.sse4_2);
-#endif  // CPU_FEATURES_OS_WINDOWS
+#endif  // !defined(CPU_FEATURES_OS_WINDOWS)
+}
+
+// https://www.felixcloutier.com/x86/cpuid#example-3-1--example-of-cache-and-tlb-interpretation
+TEST_F(CpuidX86Test, P4_CacheInfo) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000002, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x00000F0A, 0x00010808, 0x00000000, 0x3FEBFBFF}},
+      {{0x00000002, 0}, Leaf{0x665B5001, 0x00000000, 0x00000000, 0x007A7000}},
+  });
+
+  const auto info = GetX86CacheInfo();
+  EXPECT_EQ(info.size, 5);
+
+  EXPECT_EQ(info.levels[0].level, UNDEF);
+  EXPECT_EQ(info.levels[0].cache_type, CPU_FEATURE_CACHE_TLB);
+  EXPECT_EQ(info.levels[0].cache_size, 4 * KiB);
+  EXPECT_EQ(info.levels[0].ways, UNDEF);
+  EXPECT_EQ(info.levels[0].line_size, UNDEF);
+  EXPECT_EQ(info.levels[0].tlb_entries, 64);
+  EXPECT_EQ(info.levels[0].partitioning, 0);
+
+  EXPECT_EQ(info.levels[1].level, UNDEF);
+  EXPECT_EQ(info.levels[1].cache_type, CPU_FEATURE_CACHE_TLB);
+  EXPECT_EQ(info.levels[1].cache_size, 4 * KiB);
+  EXPECT_EQ(info.levels[1].ways, UNDEF);
+  EXPECT_EQ(info.levels[1].line_size, UNDEF);
+  EXPECT_EQ(info.levels[1].tlb_entries, 64);
+  EXPECT_EQ(info.levels[1].partitioning, 0);
+
+  EXPECT_EQ(info.levels[2].level, 1);
+  EXPECT_EQ(info.levels[2].cache_type, CPU_FEATURE_CACHE_DATA);
+  EXPECT_EQ(info.levels[2].cache_size, 8 * KiB);
+  EXPECT_EQ(info.levels[2].ways, 4);
+  EXPECT_EQ(info.levels[2].line_size, 64);
+  EXPECT_EQ(info.levels[2].tlb_entries, UNDEF);
+  EXPECT_EQ(info.levels[2].partitioning, 0);
+
+  EXPECT_EQ(info.levels[3].level, 1);
+  EXPECT_EQ(info.levels[3].cache_type, CPU_FEATURE_CACHE_INSTRUCTION);
+  EXPECT_EQ(info.levels[3].cache_size, 12 * KiB);
+  EXPECT_EQ(info.levels[3].ways, 8);
+  EXPECT_EQ(info.levels[3].line_size, UNDEF);
+  EXPECT_EQ(info.levels[3].tlb_entries, UNDEF);
+  EXPECT_EQ(info.levels[3].partitioning, 0);
+
+  EXPECT_EQ(info.levels[4].level, 2);
+  EXPECT_EQ(info.levels[4].cache_type, CPU_FEATURE_CACHE_DATA);
+  EXPECT_EQ(info.levels[4].cache_size, 256 * KiB);
+  EXPECT_EQ(info.levels[4].ways, 8);
+  EXPECT_EQ(info.levels[4].line_size, 64);
+  EXPECT_EQ(info.levels[4].tlb_entries, UNDEF);
+  EXPECT_EQ(info.levels[4].partitioning, 2);
 }
 
 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt
 TEST_F(CpuidX86Test, P3) {
   // Pre AVX cpus don't have xsave
-  g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+  cpu().SetOsBackupsExtendedRegisters(false);
 #if defined(CPU_FEATURES_OS_WINDOWS)
-  g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
-      PF_XMMI_INSTRUCTIONS_AVAILABLE);
-#endif  // CPU_FEATURES_OS_WINDOWS
-#if defined(CPU_FEATURES_OS_DARWIN)
-  g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
-#endif  // CPU_FEATURES_OS_DARWIN
-#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+  cpu().SetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+#elif defined(CPU_FEATURES_OS_MACOS)
+  cpu().SetDarwinSysCtlByName("hw.optional.sse");
+#elif defined(CPU_FEATURES_OS_FREEBSD)
+  auto& fs = GetEmptyFilesystem();
+  fs.CreateFile("/var/run/dmesg.boot", R"(
+  ---<<BOOT>>---
+Copyright (c) 1992-2020 The FreeBSD Project.
+FreeBSD is a registered trademark of The FreeBSD Foundation.
+  Features=0x1783fbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE>
+real memory  = 2147418112 (2047 MB)
+)");
+#elif defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
   auto& fs = GetEmptyFilesystem();
   fs.CreateFile("/proc/cpuinfo", R"(
 flags           : fpu mmx sse
 )");
-#endif  // CPU_FEATURES_OS_LINUX_OR_ANDROID
-  g_fake_cpu->SetLeaves({
+#endif
+  cpu().SetLeaves({
       {{0x00000000, 0}, Leaf{0x00000003, 0x756E6547, 0x6C65746E, 0x49656E69}},
       {{0x00000001, 0}, Leaf{0x00000673, 0x00000000, 0x00000000, 0x0387FBFF}},
       {{0x00000002, 0}, Leaf{0x03020101, 0x00000000, 0x00000000, 0x0C040843}},
@@ -506,6 +1039,7 @@
   EXPECT_EQ(info.family, 0x06);
   EXPECT_EQ(info.model, 0x07);
   EXPECT_EQ(info.stepping, 0x03);
+  EXPECT_STREQ(info.brand_string, "");
   EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::X86_UNKNOWN);
 
   char brand_string[49];
@@ -516,15 +1050,99 @@
   EXPECT_TRUE(info.features.sse);
   EXPECT_FALSE(info.features.sse2);
   EXPECT_FALSE(info.features.sse3);
-#ifndef CPU_FEATURES_OS_WINDOWS
+#if !defined(CPU_FEATURES_OS_WINDOWS)
   // Currently disabled on Windows as IsProcessorFeaturePresent do not support
   // feature detection > sse3.
   EXPECT_FALSE(info.features.ssse3);
   EXPECT_FALSE(info.features.sse4_1);
   EXPECT_FALSE(info.features.sse4_2);
-#endif  // CPU_FEATURES_OS_WINDOWS
+#endif  // !defined(CPU_FEATURES_OS_WINDOWS)
 }
 
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000480_486_CPUID.txt
+TEST_F(CpuidX86Test, INTEL_80486) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000001, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x00000480, 0x00000000, 0x00000000, 0x00000003}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x04);
+  EXPECT_EQ(info.model, 0x08);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_80486);
+}
+
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000526_P54C_CPUID.txt
+TEST_F(CpuidX86Test, INTEL_P54C) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000001, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x00000525, 0x00000000, 0x00000000, 0x000001BF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x05);
+  EXPECT_EQ(info.model, 0x02);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_P5);
+}
+
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000590_Lakemont_CPUID2.txt
+TEST_F(CpuidX86Test, INTEL_LAKEMONT) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x00000002, 0x756E6547, 0x6c65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x00000590, 0x00000000, 0x00010200, 0x8000237B}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x05);
+  EXPECT_EQ(info.model, 0x09);
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::INTEL_LAKEMONT);
+}
+
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0050670_KnightsLanding_CPUID.txt
+TEST_F(CpuidX86Test, INTEL_KNIGHTS_LANDING) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x00050670, 0x02FF0800, 0x7FF8F3BF, 0xBFEBFBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_STREQ(info.vendor, "GenuineIntel");
+  EXPECT_EQ(info.family, 0x06);
+  EXPECT_EQ(info.model, 0x57);
+  EXPECT_EQ(GetX86Microarchitecture(&info),
+            X86Microarchitecture::INTEL_KNIGHTS_L);
+}
+
+// https://github.com/google/cpu_features/issues/200
+// http://users.atw.hu/instlatx64/GenuineIntel/GenuineIntel00206F2_Eagleton_CPUID.txt
+#if defined(CPU_FEATURES_OS_WINDOWS)
+TEST_F(CpuidX86Test, WIN_INTEL_WESTMERE_EX) {
+  cpu().SetLeaves({
+      {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
+      {{0x00000001, 0}, Leaf{0x000206F2, 0x00400800, 0x02BEE3FF, 0xBFEBFBFF}},
+  });
+  const auto info = GetX86Info();
+
+  EXPECT_EQ(info.family, 0x06);
+  EXPECT_EQ(info.model, 0x2F);
+  EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_WSM);
+
+#if (_WIN32_WINNT < 0x0601)  // before Win7
+  EXPECT_FALSE(info.features.ssse3);
+  EXPECT_FALSE(info.features.sse4_1);
+  EXPECT_FALSE(info.features.sse4_2);
+#else
+  EXPECT_TRUE(info.features.ssse3);
+  EXPECT_TRUE(info.features.sse4_1);
+  EXPECT_TRUE(info.features.sse4_2);
+#endif
+}
+#endif  // CPU_FEATURES_OS_WINDOWS
+
 // TODO(user): test what happens when xsave/osxsave are not present.
 // TODO(user): test what happens when xmm/ymm/zmm os support are not
 // present.
diff --git a/test/hwcaps_for_testing.cc b/test/hwcaps_for_testing.cc
index a8086a0..fc0013d 100644
--- a/test/hwcaps_for_testing.cc
+++ b/test/hwcaps_for_testing.cc
@@ -22,25 +22,31 @@
 
 namespace {
 static auto* const g_hardware_capabilities = new HardwareCapabilities();
-static auto* const g_platform_types = new PlatformType();
+static const char* g_platform_pointer = nullptr;
+static const char* g_base_platform_pointer = nullptr;
 }  // namespace
 
 void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2) {
   g_hardware_capabilities->hwcaps = hwcaps;
   g_hardware_capabilities->hwcaps2 = hwcaps2;
 }
+void SetPlatformPointer(const char* string) { g_platform_pointer = string; }
+void SetBasePlatformPointer(const char* string) {
+  g_base_platform_pointer = string;
+}
+
+void ResetHwcaps() {
+  SetHardwareCapabilities(0, 0);
+  SetPlatformPointer(nullptr);
+  SetBasePlatformPointer(nullptr);
+}
 
 HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
   return *g_hardware_capabilities;
 }
-
-void SetPlatformTypes(const char* platform, const char* base_platform) {
-  CpuFeatures_StringView_CopyString(str(platform), g_platform_types->platform,
-                                    sizeof(g_platform_types->platform));
-  CpuFeatures_StringView_CopyString(str(base_platform),
-                                    g_platform_types->base_platform,
-                                    sizeof(g_platform_types->base_platform));
+const char* CpuFeatures_GetPlatformPointer(void) { return g_platform_pointer; }
+const char* CpuFeatures_GetBasePlatformPointer(void) {
+  return g_base_platform_pointer;
 }
 
-PlatformType CpuFeatures_GetPlatformType(void) { return *g_platform_types; }
 }  // namespace cpu_features
diff --git a/test/hwcaps_for_testing.h b/test/hwcaps_for_testing.h
index bcab82e..2138bac 100644
--- a/test/hwcaps_for_testing.h
+++ b/test/hwcaps_for_testing.h
@@ -20,7 +20,11 @@
 namespace cpu_features {
 
 void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2);
-void SetPlatformTypes(const char *platform, const char *base_platform);
+void SetPlatformPointer(const char* string);
+void SetBasePlatformPointer(const char* string);
+
+// To be called before each test.
+void ResetHwcaps();
 
 }  // namespace cpu_features
 
diff --git a/test/string_view_test.cc b/test/string_view_test.cc
index ca3e023..772ac3f 100644
--- a/test/string_view_test.cc
+++ b/test/string_view_test.cc
@@ -163,15 +163,25 @@
 TEST(StringViewTest, CpuFeatures_StringView_HasWord) {
   // Find flags at beginning, middle and end.
   EXPECT_TRUE(
-      CpuFeatures_StringView_HasWord(str("first middle last"), "first"));
+      CpuFeatures_StringView_HasWord(str("first middle last"), "first", ' '));
   EXPECT_TRUE(
-      CpuFeatures_StringView_HasWord(str("first middle last"), "middle"));
-  EXPECT_TRUE(CpuFeatures_StringView_HasWord(str("first middle last"), "last"));
+      CpuFeatures_StringView_HasWord(str("first middle last"), "middle", ' '));
+  EXPECT_TRUE(
+      CpuFeatures_StringView_HasWord(str("first middle last"), "last", ' '));
+  // Find flags at beginning, middle and end with a different separator
+  EXPECT_TRUE(
+      CpuFeatures_StringView_HasWord(str("first-middle-last"), "first", '-'));
+  EXPECT_TRUE(
+      CpuFeatures_StringView_HasWord(str("first-middle-last"), "middle", '-'));
+  EXPECT_TRUE(
+      CpuFeatures_StringView_HasWord(str("first-middle-last"), "last", '-'));
   // Do not match partial flags
   EXPECT_FALSE(
-      CpuFeatures_StringView_HasWord(str("first middle last"), "irst"));
-  EXPECT_FALSE(CpuFeatures_StringView_HasWord(str("first middle last"), "mid"));
-  EXPECT_FALSE(CpuFeatures_StringView_HasWord(str("first middle last"), "las"));
+      CpuFeatures_StringView_HasWord(str("first middle last"), "irst", ' '));
+  EXPECT_FALSE(
+      CpuFeatures_StringView_HasWord(str("first middle last"), "mid", ' '));
+  EXPECT_FALSE(
+      CpuFeatures_StringView_HasWord(str("first middle last"), "las", ' '));
 }
 
 TEST(StringViewTest, CpuFeatures_StringView_GetAttributeKeyValue) {