diff options
author | Roland Levillain <rpl@google.com> | 2022-07-20 12:20:49 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-07-20 12:20:49 +0000 |
commit | 6d28abb49625745335bfa37dc3d0e704b21cd5f3 (patch) | |
tree | 69f578706d7adc704ef6026d51ba239aa31ce2b1 | |
parent | c66fcc87775329f328a4f384796de4fbcb40b19a (diff) | |
parent | 6dd8c6baeeff0983891ecab6b47a9adc8b736ee7 (diff) | |
download | cpu_features-android14-qpr1-release.tar.gz |
Merge v0.7.0 into master. am: 47bd1869cd am: 5febe36e4e am: 4e0d6a4041 am: ea9bafd934 am: 6dd8c6baeeandroid-14.0.0_r45android-14.0.0_r44android-14.0.0_r43android-14.0.0_r42android-14.0.0_r41android-14.0.0_r40android-14.0.0_r39android-14.0.0_r38android-14.0.0_r27android-14.0.0_r26android-14.0.0_r25android-14.0.0_r24android-14.0.0_r23android-14.0.0_r22android-14.0.0_r21android-14.0.0_r20android-14.0.0_r19android-14.0.0_r18android-14.0.0_r17android-14.0.0_r16aml_rkp_341510000aml_rkp_341311000aml_rkp_341114000aml_rkp_341015010aml_rkp_341012000aml_hef_341717050aml_hef_341613000aml_hef_341512030aml_hef_341415040aml_hef_341311010aml_hef_341114030aml_cfg_341510000android14-qpr1-s2-releaseandroid14-qpr1-releaseandroid14-mainline-healthfitness-releaseandroid14-devandroid14-d2-s5-releaseandroid14-d2-s4-releaseandroid14-d2-s3-releaseandroid14-d2-s2-releaseandroid14-d2-s1-releaseandroid14-d2-release
Original change: https://android-review.googlesource.com/c/platform/external/cpu_features/+/2158718
Change-Id: I93b1c0a813747241eaff4b2c915099f1b0234555
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
70 files changed, 4519 insertions, 1658 deletions
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 @@ jobs: 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 @@ -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 @@ -101,7 +101,7 @@ cc_library { arch: { arm: { srcs: [ - "src/cpuinfo_arm.c", + "src/impl_arm_linux_or_android.c", ], whole_static_libs: [ "libcpu_features-hwcaps", @@ -109,7 +109,7 @@ cc_library { }, arm64: { srcs: [ - "src/cpuinfo_aarch64.c", + "src/impl_aarch64_linux_or_android.c", ], whole_static_libs: [ "libcpu_features-hwcaps", @@ -120,7 +120,7 @@ cc_library { }, x86: { srcs: [ - "src/cpuinfo_x86.c", + "src/impl_x86_linux_or_android.c", ], cflags: [ "-Wno-unused-variable", @@ -128,7 +128,7 @@ cc_library { }, x86_64: { srcs: [ - "src/cpuinfo_x86.c", + "src/impl_x86_linux_or_android.c", ], cflags: [ "-Wno-unused-variable", @@ -171,6 +171,16 @@ cc_binary { "-Wno-unused-function", ], }, + x86: { + cflags: [ + "-Wno-deprecated-declarations", + ], + }, + x86_64: { + cflags: [ + "-Wno-deprecated-declarations", + ], + }, }, } @@ -334,26 +344,28 @@ cc_test { 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 @@ cc_test { ], 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 @@ if(POLICY CMP0077) 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 @@ if(NOT CMAKE_BUILD_TYPE) 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 @@ set(PROCESSOR_IS_POWER FALSE) 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 @@ endif() 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 @@ add_library(utils OBJECT ${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(UNIX) 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 @@ add_library(cpu_features ${CPU_FEATURES_HDRS} ${CPU_FEATURES_SRCS}) 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 @@ install(TARGETS cpu_features list_cpu_features ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} ) install(EXPORT CpuFeaturesTargets NAMESPACE CpuFeatures:: @@ -9,11 +9,11 @@ third_party { 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 } } @@ -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 @@ instructions) at runtime. - [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 @@ instructions) at runtime. <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 @@ AES and the SSE4.2 instruction sets: ```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 @@ features and then check whether AES and NEON are supported. #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 @@ instruction set (e.g., `g++ -mavx`) and sets `has_avx` accordingly. #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 @@ set—but only if it's not Sandy Bridge. #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 @@ flags : aes,avx,cx16,smx,sse4_1,sse4_2,ssse3 <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 @@ flags : aes,avx,cx16,smx,sse4_1,sse4_2,ssse3 ## 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 @@ See [LICENSE](LICENSE) for more information. Please check the [CMake build instructions](cmake/README.md). <a name="quickstart"></a> -### Quickstart with `Ninja` +### Quickstart - - build `list_cpu_features` -``` - cmake -B/tmp/cpu_features -H. -GNinja -DCMAKE_BUILD_TYPE=Release - ninja -C/tmp/cpu_features - /tmp/cpu_features/list_cpu_features --json + - 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 ``` +_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_ @@ -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->ENV_IMG --> +<g id="edge10" class="edge"> +<title>DISTRO_IMG->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->ENV_IMG --> +<g id="edge11" class="edge"> +<title>PKG->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->DEVEL_IMG --> +<g id="edge12" class="edge"> +<title>SRC->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->INSTALL_DEVEL_IMG --> +<g id="edge7" class="edge"> +<title>SPL->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->DEVEL_IMG --> +<g id="edge1" class="edge"> +<title>ENV_IMG->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->INSTALL_ENV_IMG --> +<g id="edge4" class="edge"> +<title>ENV_IMG->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->ENV_TAR --> +<g id="edge13" class="edge"> +<title>ENV_IMG->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->BUILD_IMG --> +<g id="edge2" class="edge"> +<title>DEVEL_IMG->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->DEVEL_TAR --> +<g id="edge14" class="edge"> +<title>DEVEL_IMG->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->TEST_IMG --> +<g id="edge3" class="edge"> +<title>BUILD_IMG->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->INSTALL_ENV_IMG --> +<g id="edge5" class="edge"> +<title>BUILD_IMG->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->BUILD_TAR --> +<g id="edge15" class="edge"> +<title>BUILD_IMG->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->TEST_TAR --> +<g id="edge16" class="edge"> +<title>TEST_IMG->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->INSTALL_DEVEL_IMG --> +<g id="edge6" class="edge"> +<title>INSTALL_ENV_IMG->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->INSTALL_ENV_TAR --> +<g id="edge17" class="edge"> +<title>INSTALL_ENV_IMG->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->INSTALL_BUILD_IMG --> +<g id="edge8" class="edge"> +<title>INSTALL_DEVEL_IMG->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->INSTALL_DEVEL_TAR --> +<g id="edge18" class="edge"> +<title>INSTALL_DEVEL_IMG->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->INSTALL_TEST_IMG --> +<g id="edge9" class="edge"> +<title>INSTALL_BUILD_IMG->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->INSTALL_BUILD_TAR --> +<g id="edge19" class="edge"> +<title>INSTALL_BUILD_IMG->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->INSTALL_TEST_TAR --> +<g id="edge20" class="edge"> +<title>INSTALL_TEST_IMG->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="corentinl@google.com" +# 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="corentinl@google.com" +# 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 @@ project(googletest-download NONE) 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 @@ typedef struct { 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 @@ typedef enum { 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 @@ typedef struct { 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 @@ typedef struct { 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 @@ typedef struct { 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 @@ X86Microarchitecture GetX86Microarchitecture(const X86Info* info); // 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 @@ typedef enum { 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 @@ CPU_FEATURES_START_CPP_NAMESPACE #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 @@ typedef struct { 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 @@ void CpuFeatures_StringView_CopyString(const StringView src, char* dst, // 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 @@ install( # # 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}" \ + --target-list="${QEMU_TARGET}" \ + --audio-drv-list= \ + --disable-brlapi \ + --disable-curl \ + --disable-curses \ --disable-docs \ - --disable-sdl \ - --disable-gtk \ - --disable-gnutls \ --disable-gcrypt \ + --disable-gnutls \ + --disable-gtk \ + --disable-libnfs \ + --disable-libssh \ --disable-nettle \ - --disable-curses \ - --static + --disable-opengl \ + --disable-sdl \ + --disable-virglrenderer \ + --disable-vte \ + --enable-modules + + # --static Not supported on Archlinux + # so we use --enable-modules - make -j4 + # 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 - - 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 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 SYSROOT_FOLDER=${ARCHIVE_FOLDER}/${SYSROOT_RELATIVE_FOLDER} - local GCC_FOLDER=${ARCHIVE_FOLDER}/${GCC_RELATIVE_FOLDER} - - CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_SYSTEM_NAME=Linux) - CMAKE_ADDITIONAL_ARGS+=(-DCMAKE_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++) - - 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) - - QEMU_ARGS+=(-L ${SYSROOT_FOLDER}) - QEMU_ARGS+=(-E LD_LIBRARY_PATH=/lib) + #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 -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 -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 -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} + + # 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}) + +set(CMAKE_SYSROOT ${SYSROOT_DIR}) +set(CMAKE_STAGING_PREFIX ${STAGING_DIR}) + +set(tools ${GCC_DIR}) +set(CMAKE_C_COMPILER \${tools}/bin/${TARGET}-gcc) +set(CMAKE_CXX_COMPILER \${tools}/bin/${TARGET}-g++) + +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}" - - local GCC_FOLDER=${ARCHIVE_FOLDER}/${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 -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 + + # 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}) + +set(CMAKE_SYSROOT ${SYSROOT_DIR}) +set(CMAKE_STAGING_PREFIX ${STAGING_DIR}) + +set(tools ${GCC_DIR}) + +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 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 +} - 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}") - - local SYSROOT_FOLDER=${GCC_FOLDER}/sysroot/${FLAVOUR} - - # 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 +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[*]}" - 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) + 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" } -function expand_environment_and_integrate() { - assert_defined PROJECT_FOLDER +# 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 + 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 - integrate } -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 @@ ENVIRONMENTS=" 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 @@ bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask, #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 @@ static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) { #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 @@ HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) { return capabilities; } -PlatformType kEmptyPlatformType; - -PlatformType CpuFeatures_GetPlatformType(void) { - PlatformType type = kEmptyPlatformType; - char *platform = (char *)GetHardwareCapabilitiesFor(AT_PLATFORM); - char *base_platform = (char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM); +const char *CpuFeatures_GetPlatformPointer(void) { + return (const char *)GetHardwareCapabilitiesFor(AT_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 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 @@ static bool HandleArmLine(const LineResult result, ArmInfo* const info, 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 @@ static void FixErrors(ArmInfo* const info, // 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 @@ ArmInfo GetArmInfo(void) { 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 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 @@ static bool HandleMipsLine(const LineResult result, 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 @@ MipsInfo GetMipsInfo(void) { 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 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 @@ uint32_t GetXCR0Eax(void) { return (uint32_t)_xgetbv(0); } #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 SafeCpuIdEx(uint32_t max_cpuid_leaf, uint32_t leaf_id, int ecx) { } } -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,49 +162,574 @@ static bool HasTmmOsXSave(uint32_t xcr0_eax) { MASK_ZMM16_31 | MASK_XTILECFG | MASK_XTILEDATA); } -static bool HasSecondFMA(uint32_t model) { +//////////////////////////////////////////////////////////////////////////////// +// Vendor +//////////////////////////////////////////////////////////////////////////////// + +static void SetVendor(const Leaf leaf, char* const vendor) { + *(uint32_t*)(vendor) = leaf.ebx; + *(uint32_t*)(vendor + 4) = leaf.edx; + *(uint32_t*)(vendor + 8) = leaf.ecx; + vendor[12] = '\0'; +} + +static int IsVendor(const Leaf leaf, const char* const name) { + const uint32_t ebx = *(const uint32_t*)(name); + const uint32_t edx = *(const uint32_t*)(name + 4); + const uint32_t ecx = *(const uint32_t*)(name + 8); + 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 (model == 0x55) { - char proc_name[49] = {0}; - FillX86BrandString(proc_name); + if (info->model == 0x55) { // detect Xeon - if (proc_name[9] == 'X') { + if (info->brand_string[9] == 'X') { // detect Silver or Bronze - if (proc_name[17] == 'S' || proc_name[17] == 'B') return false; + if (info->brand_string[17] == 'S' || info->brand_string[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')); + 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 (proc_name[17] == 'W' && proc_name[21] == '0') return false; + if (info->brand_string[17] == 'W' && info->brand_string[21] == '0') + return false; // detect Xeon D 2xxx - if (proc_name[17] == 'D' && proc_name[19] == '2' && proc_name[20] == '1') + if (info->brand_string[17] == 'D' && info->brand_string[19] == '2' && + info->brand_string[20] == '1') return false; } return true; } // Cannon Lake client - if (model == 0x66) return false; + if (info->model == 0x66) return false; // Ice Lake client - if (model == 0x7d || model == 0x7e) return false; + if (info->model == 0x7d || info->model == 0x7e) return false; // This is the right default... return true; } -static void SetVendor(const Leaf leaf, char* const vendor) { - *(uint32_t*)(vendor) = leaf.ebx; - *(uint32_t*)(vendor + 4) = leaf.edx; - *(uint32_t*)(vendor + 8) = leaf.ecx; - vendor[12] = '\0'; +// 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 int IsVendor(const Leaf leaf, const char* const name) { - const uint32_t ebx = *(const uint32_t*)(name); - const uint32_t edx = *(const uint32_t*)(name + 4); - const uint32_t ecx = *(const uint32_t*)(name + 8); - return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx; +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 CacheLevelInfo GetCacheLevelInfo(const uint32_t reg) { } } -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); - } -} - -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]); - } +// 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; + + 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++; - } -} - -// 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); + ++info.size; } -} - -// 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); - } - 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; + 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 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'; + return info; } //////////////////////////////////////////////////////////////////////////////// -// Introspection functions - -int GetX86FeaturesEnumValue(const X86Features* features, - X86FeaturesEnum value) { - if (value >= X86_LAST_) return false; - return kGetters[value](features); -} - -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"; +// 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" + +#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* 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 @@ int CpuFeatures_StringView_IndexOf(const StringView view, 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 @@ void CpuFeatures_StringView_CopyString(const StringView src, char* dst, 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 @@ bool CpuFeatures_StringView_HasWord(const StringView line, 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 @@ static Node* GetCacheTypeString(CacheType cache_type) { 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 @@ add_test(NAME stack_line_reader_test COMMAND stack_line_reader_test) ##------------------------------------------------------------------------------ ## 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 @@ endif() ##------------------------------------------------------------------------------ ## 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 @@ namespace { 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, FromHardwareCap) { } TEST(CpuinfoAarch64Test, FromHardwareCap2) { + ResetHwcaps(); SetHardwareCapabilities(AARCH64_HWCAP_FP, AARCH64_HWCAP2_SVE2 | AARCH64_HWCAP2_BTI); GetEmptyFilesystem(); // disabling /proc/cpuinfo @@ -90,7 +92,7 @@ TEST(CpuinfoAarch64Test, FromHardwareCap2) { } TEST(CpuinfoAarch64Test, ARMCortexA53) { - DisableHardwareCapabilities(); + ResetHwcaps(); auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"(Processor : AArch64 Processor rev 3 (aarch64) @@ -165,6 +167,7 @@ CPU revision : 3)"); 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, FromHardwareCap) { } 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 @@ CPU revision : 3)"); // 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 @@ Serial : 000000006cd946f3)"); } 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 @@ Serial : 0000000000000000)"); // 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 @@ Serial : 33323613546d00ec )"); // 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 @@ Serial: 00001e030000354e)"); // 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 @@ CPU revision : 2)"); // 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 @@ CPU revision : 3)"); 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 cpu_features { 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, FromHardwareCapBoth) { } TEST(CpuinfoMipsTest, FromHardwareCapOnlyOne) { + ResetHwcaps(); SetHardwareCapabilities(MIPS_HWCAP_MSA, 0); GetEmptyFilesystem(); // disabling /proc/cpuinfo const auto info = GetMipsInfo(); @@ -44,7 +44,7 @@ TEST(CpuinfoMipsTest, FromHardwareCapOnlyOne) { } 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 @@ VPE : 0 } TEST(CpuinfoMipsTest, AR7161) { - DisableHardwareCapabilities(); + ResetHwcaps(); auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"(system type : Atheros AR7161 rev 2 @@ -98,7 +98,7 @@ VCEI exceptions : not available } 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, FromHardwareCap) { } TEST(CpustringsPPCTest, Blade) { - DisableHardwareCapabilities(); + ResetHwcaps(); auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"(processor : 14 @@ -57,7 +56,8 @@ timebase : 512000000 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 @@ machine : CHRP IBM,8406-70Y)"); } TEST(CpustringsPPCTest, Firestone) { - DisableHardwareCapabilities(); + ResetHwcaps(); auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"(processor : 126 @@ -94,7 +94,7 @@ firmware : OPAL v3)"); } 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 @@ class FakeCpu { 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 @@ class FakeCpu { 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 @@ class FakeCpu { 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 @@ namespace { 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 @@ TEST_F(CpuidX86Test, SandyBridge) { 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, SkyLake) { } 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, Branding) { } 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, KabyLakeCache) { } 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 @@ TEST_F(CpuidX86Test, HSWCache) { 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 @@ TEST_F(CpuidX86Test, AMD_K15) { 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 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 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 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 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 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 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 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 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 @@ flags : fpu mmx sse 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 @@ flags : fpu mmx sse 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 cpu_features { 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_CopyString) { 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) { |