--- /dev/null
+---
+Language: Cpp
+BasedOnStyle: Google
+...
--- /dev/null
+# Create a virtual environment with all tools installed
+# ref: https://hub.docker.com/_/alpine
+FROM alpine:edge
+# Install system build dependencies
+RUN apk add --no-cache git clang
--- /dev/null
+name: clang-format Check
+
+on: [push, pull_request]
+
+jobs:
+ # Building using the github runner environement directly.
+ clang-format:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Fetch origin/master
+ run: git fetch origin master
+ - name: List of changed file(s)
+ run: git diff --name-only FETCH_HEAD
+
+ - name: Build clang-format docker
+ run: cd .github/workflows && docker build --tag=linter .
+ - name: Check clang-format
+ run: docker run --rm --init -v $(pwd):/repo linter:latest clang-format --version
+ - name: clang-format help
+ run: docker run --rm --init -v $(pwd):/repo linter:latest clang-format --help
+
+ - name: Check current commit
+ run: docker run --rm --init -v $(pwd):/repo -w /repo linter:latest sh -c "git diff --diff-filter=d --name-only FETCH_HEAD | grep '\.c$\|\.h$\|\.cc$' | xargs clang-format --style=file --dry-run --Werror "
--- /dev/null
+cmake_build/
+build/
+
+*.swp
--- /dev/null
+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
--- /dev/null
+cmake_minimum_required(VERSION 3.0)
+
+# option() honors normal variables.
+# see: https://cmake.org/cmake/help/git-stage/policy/CMP0077.html
+if(POLICY CMP0077)
+ cmake_policy(SET CMP0077 NEW)
+endif()
+
+project(CpuFeatures VERSION 0.6.0 LANGUAGES C)
+
+set(CMAKE_C_STANDARD 99)
+
+# Default Build Type to be Release
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+ "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
+ 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)
+endif()
+
+include(CheckIncludeFile)
+include(CheckSymbolExists)
+include(GNUInstallDirs)
+
+macro(setup_include_and_definitions TARGET_NAME)
+ target_include_directories(${TARGET_NAME}
+ PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+ PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/internal>
+ )
+ target_compile_definitions(${TARGET_NAME}
+ PUBLIC STACK_LINE_READER_BUFFER_SIZE=1024
+ )
+endmacro()
+
+set(PROCESSOR_IS_MIPS FALSE)
+set(PROCESSOR_IS_ARM FALSE)
+set(PROCESSOR_IS_AARCH64 FALSE)
+set(PROCESSOR_IS_X86 FALSE)
+set(PROCESSOR_IS_POWER FALSE)
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
+ set(PROCESSOR_IS_MIPS 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)")
+ set(PROCESSOR_IS_POWER TRUE)
+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)
+ 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()
+endmacro()
+
+#
+# library : utils
+#
+
+add_library(utils OBJECT
+ ${PROJECT_SOURCE_DIR}/include/internal/bit_utils.h
+ ${PROJECT_SOURCE_DIR}/include/internal/filesystem.h
+ ${PROJECT_SOURCE_DIR}/include/internal/stack_line_reader.h
+ ${PROJECT_SOURCE_DIR}/include/internal/string_view.h
+ ${PROJECT_SOURCE_DIR}/src/filesystem.c
+ ${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)
+
+#
+# library : unix_based_hardware_detection
+#
+
+if(UNIX)
+ add_library(unix_based_hardware_detection OBJECT
+ ${PROJECT_SOURCE_DIR}/include/internal/hwcaps.h
+ ${PROJECT_SOURCE_DIR}/src/hwcaps.c
+ )
+ setup_include_and_definitions(unix_based_hardware_detection)
+ check_include_file(dlfcn.h HAVE_DLFCN_H)
+ if(HAVE_DLFCN_H)
+ target_compile_definitions(unix_based_hardware_detection PRIVATE HAVE_DLFCN_H)
+ endif()
+ check_symbol_exists(getauxval "sys/auxv.h" HAVE_STRONG_GETAUXVAL)
+ 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()
+
+#
+# library : cpu_features
+#
+set (CPU_FEATURES_HDRS)
+set (CPU_FEATURES_SRCS)
+add_cpu_features_headers_and_sources(CPU_FEATURES_HDRS CPU_FEATURES_SRCS)
+list(APPEND CPU_FEATURES_SRCS $<TARGET_OBJECTS:utils>)
+if(NOT PROCESSOR_IS_X86 AND UNIX)
+ list(APPEND CPU_FEATURES_SRCS $<TARGET_OBJECTS:unix_based_hardware_detection>)
+endif()
+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>
+)
+if(PROCESSOR_IS_X86)
+ if(APPLE)
+ target_compile_definitions(cpu_features PRIVATE HAVE_SYSCTLBYNAME)
+ endif()
+endif()
+add_library(CpuFeature::cpu_features ALIAS cpu_features)
+
+#
+# program : list_cpu_features
+#
+
+add_executable(list_cpu_features ${PROJECT_SOURCE_DIR}/src/utils/list_cpu_features.c)
+target_link_libraries(list_cpu_features PRIVATE cpu_features)
+add_executable(CpuFeature::list_cpu_features ALIAS list_cpu_features)
+
+#
+# ndk_compat
+#
+
+if(ANDROID)
+add_subdirectory(ndk_compat)
+endif()
+
+#
+# tests
+#
+
+include(CTest)
+if(BUILD_TESTING)
+ # Automatically incorporate googletest into the CMake Project if target not
+ # found.
+ enable_language(CXX)
+
+ set(CMAKE_CXX_STANDARD 11)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+ set(CMAKE_CXX_EXTENSIONS OFF) # prefer use of -std11 instead of -gnustd11
+
+ if(NOT TARGET gtest OR NOT TARGET gmock_main)
+ # Download and unpack googletest at configure time.
+ configure_file(
+ cmake/googletest.CMakeLists.txt.in
+ googletest-download/CMakeLists.txt
+ )
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
+
+ if(result)
+ message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+ endif()
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
+
+ if(result)
+ message(FATAL_ERROR "Build step for googletest failed: ${result}")
+ endif()
+
+ # Prevent overriding the parent project's compiler/linker settings on
+ # Windows.
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+
+ # Add googletest directly to our build. This defines the gtest and
+ # gtest_main targets.
+ add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
+ ${CMAKE_BINARY_DIR}/googletest-build
+ EXCLUDE_FROM_ALL)
+ endif()
+
+ add_subdirectory(test)
+endif()
+
+#
+# Install cpu_features and list_cpu_features
+#
+
+include(GNUInstallDirs)
+install(TARGETS cpu_features list_cpu_features
+ EXPORT CpuFeaturesTargets
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+install(EXPORT CpuFeaturesTargets
+ NAMESPACE CpuFeatures::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures
+ COMPONENT Devel
+)
+include(CMakePackageConfigHelpers)
+configure_package_config_file(cmake/CpuFeaturesConfig.cmake.in
+ "${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
+ INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
+ NO_SET_AND_CHECK_MACRO
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+)
+write_basic_package_version_file(
+ "${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
+ COMPATIBILITY SameMajorVersion
+)
+install(
+ FILES
+ "${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
+ "${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
+ COMPONENT Devel
+)
--- /dev/null
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+--------------------------------------------------------------------------------
+For files in the `ndk_compat` folder:
+--------------------------------------------------------------------------------
+
+Copyright (C) 2010 The Android Open Source Project
+All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
--- /dev/null
+# cpu_features [](https://travis-ci.org/google/cpu_features) [](https://ci.appveyor.com/project/gchatelet/cpu-features/branch/master)
+
+A cross-platform C library to retrieve CPU features (such as available
+instructions) at runtime.
+
+## Table of Contents
+
+- [Design Rationale](#rationale)
+- [Code samples](#codesample)
+- [Running sample code](#usagesample)
+- [What's supported](#support)
+- [Android NDK's drop in replacement](#ndk)
+- [License](#license)
+- [Build with cmake](#cmake)
+
+<a name="rationale"></a>
+## Design Rationale
+
+- **Simple to use.** See the snippets below for examples.
+- **Extensible.** Easy to add missing features or architectures.
+- **Compatible with old compilers** and available on many architectures so it
+ can be used widely. To ensure that cpu_features works on as many platforms
+ as possible, we implemented it in a highly portable version of C: C99.
+- **Sandbox-compatible.** The library uses a variety of strategies to cope
+ with sandboxed environments or when `cpuid` is unavailable. This is useful
+ when running integration tests in hermetic environments.
+- **Thread safe, no memory allocation, and raises no exceptions.**
+ cpu_features is suitable for implementing fundamental libc functions like
+ `malloc`, `memcpy`, and `memcmp`.
+- **Unit tested.**
+
+<a name="codesample"></a>
+## Code samples
+
+**Note:** For C++ code, the library functions are defined in the `CpuFeatures` namespace.
+
+### Checking features at runtime
+
+Here's a simple example that executes a codepath if the CPU supports both the
+AES and the SSE4.2 instruction sets:
+
+```c
+#include "cpuinfo_x86.h"
+
+// For C++, add `using namespace CpuFeatures;`
+static const X86Features features = GetX86Info().features;
+
+void Compute(void) {
+ if (features.aes && features.sse4_2) {
+ // Run optimized code.
+ } else {
+ // Run standard code.
+ }
+}
+```
+
+### Caching for faster evaluation of complex checks
+
+If you wish, you can read all the features at once into a global variable, and
+then query for the specific features you care about. Below, we store all the ARM
+features and then check whether AES and NEON are supported.
+
+```c
+#include <stdbool.h>
+#include "cpuinfo_arm.h"
+
+// For C++, add `using namespace CpuFeatures;`
+static const ArmFeatures features = GetArmInfo().features;
+static const bool has_aes_and_neon = features.aes && features.neon;
+
+// use has_aes_and_neon.
+```
+
+This is a good approach to take if you're checking for combinations of features
+when using a compiler that is slow to extract individual bits from bit-packed
+structures.
+
+### Checking compile time flags
+
+The following code determines whether the compiler was told to use the AVX
+instruction set (e.g., `g++ -mavx`) and sets `has_avx` accordingly.
+
+```c
+#include <stdbool.h>
+#include "cpuinfo_x86.h"
+
+// For C++, add `using namespace CpuFeatures;`
+static const X86Features features = GetX86Info().features;
+static const bool has_avx = CPU_FEATURES_COMPILED_X86_AVX || features.avx;
+
+// use has_avx.
+```
+
+`CPU_FEATURES_COMPILED_X86_AVX` is set to 1 if the compiler was instructed to
+use AVX and 0 otherwise, combining compile time and runtime knowledge.
+
+### Rejecting poor hardware implementations based on microarchitecture
+
+On x86, the first incarnation of a feature in a microarchitecture might not be
+the most efficient (e.g. AVX on Sandy Bridge). We provide a function to retrieve
+the underlying microarchitecture so you can decide whether to use it.
+
+Below, `has_fast_avx` is set to 1 if the CPU supports the AVX instruction
+set—but only if it's not Sandy Bridge.
+
+```c
+#include <stdbool.h>
+#include "cpuinfo_x86.h"
+
+// For C++, add `using namespace CpuFeatures;`
+static const X86Info info = GetX86Info();
+static const X86Microarchitecture uarch = GetX86Microarchitecture(&info);
+static const bool has_fast_avx = info.features.avx && uarch != INTEL_SNB;
+
+// use has_fast_avx.
+```
+
+This feature is currently available only for x86 microarchitectures.
+
+<a name="usagesample"></a>
+### Running sample code
+
+Building `cpu_features` (check [quickstart](#quickstart) below) brings a small executable to test the library.
+
+```shell
+ % ./build/list_cpu_features
+arch : x86
+brand : Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz
+family : 6 (0x06)
+model : 45 (0x2D)
+stepping : 7 (0x07)
+uarch : INTEL_SNB
+flags : aes,avx,cx16,smx,sse4_1,sse4_2,ssse3
+```
+
+```shell
+% ./build/list_cpu_features --json
+{"arch":"x86","brand":" Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz","family":6,"model":45,"stepping":7,"uarch":"INTEL_SNB","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 |
+
+1. **Features revealed from Linux.** We gather data from several sources
+ depending on availability:
+ + from glibc's
+ [getauxval](https://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html)
+ + by parsing `/proc/self/auxv`
+ + by parsing `/proc/cpuinfo`
+2. **Features revealed from CPU.** features are retrieved by using the `cpuid`
+ instruction.
+3. **Microarchitecture detection.** On x86 some features are not always
+ implemented efficiently in hardware (e.g. AVX on Sandybridge). Exposing the
+ microarchitecture allows the client to reject particular microarchitectures.
+4. All flavors of Mips are supported, little and big endian as well as 32/64
+ bits.
+
+<a name="ndk"></a>
+## 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)
+, see [ndk_compat](ndk_compat) folder for details.
+
+<a name="license"></a>
+## License
+
+The cpu_features library is licensed under the terms of the Apache license.
+See [LICENSE](LICENSE) for more information.
+
+<a name="cmake"></a>
+## Build with CMake
+
+Please check the [CMake build instructions](cmake/README.md).
+
+<a name="quickstart"></a>
+### Quickstart with `Ninja`
+
+ - 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 tests
+```
+ cmake -B/tmp/cpu_features -H. -GNinja -DBUILD_TESTING=ON
+ ninja -C/tmp/cpu_features
+ ninja -C/tmp/cpu_features test
+```
--- /dev/null
+# ===== googletest =====
+
+git_repository(
+ name = "com_google_googletest",
+ remote = "https://github.com/google/googletest.git",
+ commit = "c3f65335b79f47b05629e79a54685d899bc53b93",
+)
--- /dev/null
+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
--- /dev/null
+# CpuFeatures CMake configuration file
+
+include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesTargets.cmake")
--- /dev/null
+# CpuFeaturesNdkCompat CMake configuration file
+
+include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesNdkCompatTargets.cmake")
--- /dev/null
+# CMake build instructions
+
+## 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.
+
+ This is similar to the recommended usage of the googletest framework
+ ( https://github.com/google/googletest/blob/master/googletest/README.md )
+
+ 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
+
+ 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.
+
+## Enabling 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.
--- /dev/null
+cmake_minimum_required(VERSION 2.8.2)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG master
+ SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+)
\ No newline at end of file
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef enum {
+ CPU_FEATURE_CACHE_NULL = 0,
+ CPU_FEATURE_CACHE_DATA = 1,
+ CPU_FEATURE_CACHE_INSTRUCTION = 2,
+ CPU_FEATURE_CACHE_UNIFIED = 3,
+ CPU_FEATURE_CACHE_TLB = 4,
+ CPU_FEATURE_CACHE_DTLB = 5,
+ CPU_FEATURE_CACHE_STLB = 6,
+ CPU_FEATURE_CACHE_PREFETCH = 7
+} CacheType;
+
+typedef struct {
+ int level;
+ CacheType cache_type;
+ int cache_size; // Cache size in bytes
+ int ways; // Associativity, 0 undefined, 0xFF fully associative
+ int line_size; // Cache line size in bytes
+ int tlb_entries; // number of entries for TLB
+ int partitioning; // number of lines per sector
+} CacheLevelInfo;
+
+// Increase this value if more cache levels are needed.
+#ifndef CPU_FEATURES_MAX_CACHE_LEVEL
+#define CPU_FEATURES_MAX_CACHE_LEVEL 10
+#endif
+typedef struct {
+ int size;
+ CacheLevelInfo levels[CPU_FEATURES_MAX_CACHE_LEVEL];
+} CacheInfo;
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
+#define CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
+
+////////////////////////////////////////////////////////////////////////////////
+// Architectures
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__pnacl__) || defined(__CLR_VER)
+#define CPU_FEATURES_ARCH_VM
+#endif
+
+#if (defined(_M_IX86) || defined(__i386__)) && !defined(CPU_FEATURES_ARCH_VM)
+#define CPU_FEATURES_ARCH_X86_32
+#endif
+
+#if (defined(_M_X64) || defined(__x86_64__)) && !defined(CPU_FEATURES_ARCH_VM)
+#define CPU_FEATURES_ARCH_X86_64
+#endif
+
+#if defined(CPU_FEATURES_ARCH_X86_32) || defined(CPU_FEATURES_ARCH_X86_64)
+#define CPU_FEATURES_ARCH_X86
+#endif
+
+#if (defined(__arm__) || defined(_M_ARM))
+#define CPU_FEATURES_ARCH_ARM
+#endif
+
+#if defined(__aarch64__)
+#define CPU_FEATURES_ARCH_AARCH64
+#endif
+
+#if (defined(CPU_FEATURES_ARCH_AARCH64) || defined(CPU_FEATURES_ARCH_ARM))
+#define CPU_FEATURES_ARCH_ANY_ARM
+#endif
+
+#if defined(__mips64)
+#define CPU_FEATURES_ARCH_MIPS64
+#endif
+
+#if defined(__mips__) && !defined(__mips64) // mips64 also declares __mips__
+#define CPU_FEATURES_ARCH_MIPS32
+#endif
+
+#if defined(CPU_FEATURES_ARCH_MIPS32) || defined(CPU_FEATURES_ARCH_MIPS64)
+#define CPU_FEATURES_ARCH_MIPS
+#endif
+
+#if defined(__powerpc__)
+#define CPU_FEATURES_ARCH_PPC
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Os
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__linux__)
+#define CPU_FEATURES_OS_LINUX_OR_ANDROID
+#endif
+
+#if defined(__ANDROID__)
+#define CPU_FEATURES_OS_ANDROID
+#endif
+
+#if (defined(_WIN64) || defined(_WIN32))
+#define CPU_FEATURES_OS_WINDOWS
+#endif
+
+#if (defined(__apple__) || defined(__APPLE__) || defined(__MACH__))
+#define CPU_FEATURES_OS_DARWIN
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Compilers
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__clang__)
+#define CPU_FEATURES_COMPILER_CLANG
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+#define CPU_FEATURES_COMPILER_GCC
+#endif
+
+#if defined(_MSC_VER)
+#define CPU_FEATURES_COMPILER_MSC
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cpp
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(__cplusplus)
+#define CPU_FEATURES_START_CPP_NAMESPACE \
+ namespace cpu_features { \
+ extern "C" {
+#define CPU_FEATURES_END_CPP_NAMESPACE \
+ } \
+ }
+#else
+#define CPU_FEATURES_START_CPP_NAMESPACE
+#define CPU_FEATURES_END_CPP_NAMESPACE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Compiler flags
+////////////////////////////////////////////////////////////////////////////////
+
+// Use the following to check if a feature is known to be available at
+// compile time. See README.md for an example.
+#if defined(CPU_FEATURES_ARCH_X86)
+
+#if defined(__AES__)
+#define CPU_FEATURES_COMPILED_X86_AES 1
+#else
+#define CPU_FEATURES_COMPILED_X86_AES 0
+#endif // defined(__AES__)
+
+#if defined(__F16C__)
+#define CPU_FEATURES_COMPILED_X86_F16C 1
+#else
+#define CPU_FEATURES_COMPILED_X86_F16C 0
+#endif // defined(__F16C__)
+
+#if defined(__BMI__)
+#define CPU_FEATURES_COMPILED_X86_BMI 1
+#else
+#define CPU_FEATURES_COMPILED_X86_BMI 0
+#endif // defined(__BMI__)
+
+#if defined(__BMI2__)
+#define CPU_FEATURES_COMPILED_X86_BMI2 1
+#else
+#define CPU_FEATURES_COMPILED_X86_BMI2 0
+#endif // defined(__BMI2__)
+
+#if (defined(__SSE__) || (_M_IX86_FP >= 1))
+#define CPU_FEATURES_COMPILED_X86_SSE 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSE 0
+#endif
+
+#if (defined(__SSE2__) || (_M_IX86_FP >= 2))
+#define CPU_FEATURES_COMPILED_X86_SSE2 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSE2 0
+#endif
+
+#if defined(__SSE3__)
+#define CPU_FEATURES_COMPILED_X86_SSE3 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSE3 0
+#endif // defined(__SSE3__)
+
+#if defined(__SSSE3__)
+#define CPU_FEATURES_COMPILED_X86_SSSE3 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSSE3 0
+#endif // defined(__SSSE3__)
+
+#if defined(__SSE4_1__)
+#define CPU_FEATURES_COMPILED_X86_SSE4_1 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSE4_1 0
+#endif // defined(__SSE4_1__)
+
+#if defined(__SSE4_2__)
+#define CPU_FEATURES_COMPILED_X86_SSE4_2 1
+#else
+#define CPU_FEATURES_COMPILED_X86_SSE4_2 0
+#endif // defined(__SSE4_2__)
+
+#if defined(__AVX__)
+#define CPU_FEATURES_COMPILED_X86_AVX 1
+#else
+#define CPU_FEATURES_COMPILED_X86_AVX 0
+#endif // defined(__AVX__)
+
+#if defined(__AVX2__)
+#define CPU_FEATURES_COMPILED_X86_AVX2 1
+#else
+#define CPU_FEATURES_COMPILED_X86_AVX2 0
+#endif // defined(__AVX2__)
+
+#endif // defined(CPU_FEATURES_ARCH_X86)
+
+#if defined(CPU_FEATURES_ARCH_ANY_ARM)
+#if defined(__ARM_NEON__)
+#define CPU_FEATURES_COMPILED_ANY_ARM_NEON 1
+#else
+#define CPU_FEATURES_COMPILED_ANY_ARM_NEON 0
+#endif // defined(__ARM_NEON__)
+#endif // defined(CPU_FEATURES_ARCH_ANY_ARM)
+
+#if defined(CPU_FEATURES_ARCH_MIPS)
+#if defined(__mips_msa)
+#define CPU_FEATURES_COMPILED_MIPS_MSA 1
+#else
+#define CPU_FEATURES_COMPILED_MIPS_MSA 0
+#endif // defined(__mips_msa)
+#endif // defined(CPU_FEATURES_ARCH_MIPS)
+
+#endif // CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ int fp : 1; // Floating-point.
+ int asimd : 1; // Advanced SIMD.
+ int evtstrm : 1; // Generic timer generated events.
+ int aes : 1; // Hardware-accelerated Advanced Encryption Standard.
+ int pmull : 1; // Polynomial multiply long.
+ int sha1 : 1; // Hardware-accelerated SHA1.
+ int sha2 : 1; // Hardware-accelerated SHA2-256.
+ int crc32 : 1; // Hardware-accelerated CRC-32.
+ int atomics : 1; // Armv8.1 atomic instructions.
+ int fphp : 1; // Half-precision floating point support.
+ int asimdhp : 1; // Advanced SIMD half-precision support.
+ int cpuid : 1; // Access to certain ID registers.
+ int asimdrdm : 1; // Rounding Double Multiply Accumulate/Subtract.
+ int jscvt : 1; // Support for JavaScript conversion.
+ int fcma : 1; // Floating point complex numbers.
+ int lrcpc : 1; // Support for weaker release consistency.
+ int dcpop : 1; // Data persistence writeback.
+ int sha3 : 1; // Hardware-accelerated SHA3.
+ int sm3 : 1; // Hardware-accelerated SM3.
+ int sm4 : 1; // Hardware-accelerated SM4.
+ int asimddp : 1; // Dot product instruction.
+ int sha512 : 1; // Hardware-accelerated SHA512.
+ int sve : 1; // Scalable Vector Extension.
+ int asimdfhm : 1; // Additional half-precision instructions.
+ int dit : 1; // Data independent timing.
+ int uscat : 1; // Unaligned atomics support.
+ int ilrcpc : 1; // Additional support for weaker release consistency.
+ int flagm : 1; // Flag manipulation instructions.
+ int ssbs : 1; // Speculative Store Bypass Safe PSTATE bit.
+ int sb : 1; // Speculation barrier.
+ int paca : 1; // Address authentication.
+ int pacg : 1; // Generic authentication.
+ int dcpodp : 1; // Data cache clean to point of persistence.
+ int sve2 : 1; // Scalable Vector Extension (version 2).
+ int sveaes : 1; // SVE AES instructions.
+ int svepmull : 1; // SVE polynomial multiply long instructions.
+ int svebitperm : 1; // SVE bit permute instructions.
+ int svesha3 : 1; // SVE SHA3 instructions.
+ int svesm4 : 1; // SVE SM4 instructions.
+ int flagm2 : 1; // Additional flag manipulation instructions.
+ int frint : 1; // Floating point to integer rounding.
+ int svei8mm : 1; // SVE Int8 matrix multiplication instructions.
+ int svef32mm : 1; // SVE FP32 matrix multiplication instruction.
+ int svef64mm : 1; // SVE FP64 matrix multiplication instructions.
+ int svebf16 : 1; // SVE BFloat16 instructions.
+ int i8mm : 1; // Int8 matrix multiplication instructions.
+ int bf16 : 1; // BFloat16 instructions.
+ int dgh : 1; // Data Gathering Hint instruction.
+ int rng : 1; // True random number generator support.
+ int bti : 1; // Branch target identification.
+
+ // Make sure to update Aarch64FeaturesEnum below if you add a field here.
+} Aarch64Features;
+
+typedef struct {
+ Aarch64Features features;
+ int implementer;
+ int variant;
+ int part;
+ int revision;
+} Aarch64Info;
+
+Aarch64Info GetAarch64Info(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+ AARCH64_FP,
+ AARCH64_ASIMD,
+ AARCH64_EVTSTRM,
+ AARCH64_AES,
+ AARCH64_PMULL,
+ AARCH64_SHA1,
+ AARCH64_SHA2,
+ AARCH64_CRC32,
+ AARCH64_ATOMICS,
+ AARCH64_FPHP,
+ AARCH64_ASIMDHP,
+ AARCH64_CPUID,
+ AARCH64_ASIMDRDM,
+ AARCH64_JSCVT,
+ AARCH64_FCMA,
+ AARCH64_LRCPC,
+ AARCH64_DCPOP,
+ AARCH64_SHA3,
+ AARCH64_SM3,
+ AARCH64_SM4,
+ AARCH64_ASIMDDP,
+ AARCH64_SHA512,
+ AARCH64_SVE,
+ AARCH64_ASIMDFHM,
+ AARCH64_DIT,
+ AARCH64_USCAT,
+ AARCH64_ILRCPC,
+ AARCH64_FLAGM,
+ AARCH64_SSBS,
+ AARCH64_SB,
+ AARCH64_PACA,
+ AARCH64_PACG,
+ AARCH64_DCPODP,
+ AARCH64_SVE2,
+ AARCH64_SVEAES,
+ AARCH64_SVEPMULL,
+ AARCH64_SVEBITPERM,
+ AARCH64_SVESHA3,
+ AARCH64_SVESM4,
+ AARCH64_FLAGM2,
+ AARCH64_FRINT,
+ AARCH64_SVEI8MM,
+ AARCH64_SVEF32MM,
+ AARCH64_SVEF64MM,
+ AARCH64_SVEBF16,
+ AARCH64_I8MM,
+ AARCH64_BF16,
+ AARCH64_DGH,
+ AARCH64_RNG,
+ AARCH64_BTI,
+ AARCH64_LAST_,
+} Aarch64FeaturesEnum;
+
+int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
+ Aarch64FeaturesEnum value);
+
+const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#if !defined(CPU_FEATURES_ARCH_AARCH64)
+#error "Including cpuinfo_aarch64.h from a non-aarch64 target."
+#endif
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
+
+#include <stdint.h> // uint32_t
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ int swp : 1; // SWP instruction (atomic read-modify-write)
+ int half : 1; // Half-word loads and stores
+ int thumb : 1; // Thumb (16-bit instruction set)
+ int _26bit : 1; // "26 Bit" Model (Processor status register folded into
+ // program counter)
+ int fastmult : 1; // 32x32->64-bit multiplication
+ int fpa : 1; // Floating point accelerator
+ int vfp : 1; // Vector Floating Point.
+ int edsp : 1; // DSP extensions (the 'e' variant of the ARM9 CPUs, and all
+ // others above)
+ int java : 1; // Jazelle (Java bytecode accelerator)
+ int iwmmxt : 1; // Intel Wireless MMX Technology.
+ int crunch : 1; // MaverickCrunch coprocessor
+ int thumbee : 1; // ThumbEE
+ int neon : 1; // Advanced SIMD.
+ int vfpv3 : 1; // VFP version 3
+ int vfpv3d16 : 1; // VFP version 3 with 16 D-registers
+ int tls : 1; // TLS register
+ int vfpv4 : 1; // VFP version 4 with fast context switching
+ int idiva : 1; // SDIV and UDIV hardware division in ARM mode.
+ int idivt : 1; // SDIV and UDIV hardware division in Thumb mode.
+ int vfpd32 : 1; // VFP with 32 D-registers
+ int lpae : 1; // Large Physical Address Extension (>4GB physical memory on
+ // 32-bit architecture)
+ int evtstrm : 1; // kernel event stream using generic architected timer
+ int aes : 1; // Hardware-accelerated Advanced Encryption Standard.
+ int pmull : 1; // Polynomial multiply long.
+ int sha1 : 1; // Hardware-accelerated SHA1.
+ int sha2 : 1; // Hardware-accelerated SHA2-256.
+ int crc32 : 1; // Hardware-accelerated CRC-32.
+
+ // Make sure to update ArmFeaturesEnum below if you add a field here.
+} ArmFeatures;
+
+typedef struct {
+ ArmFeatures features;
+ int implementer;
+ int architecture;
+ int variant;
+ int part;
+ int revision;
+} ArmInfo;
+
+// TODO(user): Add macros to know which features are present at compile
+// time.
+
+ArmInfo GetArmInfo(void);
+
+// Compute CpuId from ArmInfo.
+uint32_t GetArmCpuId(const ArmInfo* const info);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+ ARM_SWP,
+ ARM_HALF,
+ ARM_THUMB,
+ ARM_26BIT,
+ ARM_FASTMULT,
+ ARM_FPA,
+ ARM_VFP,
+ ARM_EDSP,
+ ARM_JAVA,
+ ARM_IWMMXT,
+ ARM_CRUNCH,
+ ARM_THUMBEE,
+ ARM_NEON,
+ ARM_VFPV3,
+ ARM_VFPV3D16,
+ ARM_TLS,
+ ARM_VFPV4,
+ ARM_IDIVA,
+ ARM_IDIVT,
+ ARM_VFPD32,
+ ARM_LPAE,
+ ARM_EVTSTRM,
+ ARM_AES,
+ ARM_PMULL,
+ ARM_SHA1,
+ ARM_SHA2,
+ ARM_CRC32,
+ ARM_LAST_,
+} ArmFeaturesEnum;
+
+int GetArmFeaturesEnumValue(const ArmFeatures* features, ArmFeaturesEnum value);
+
+const char* GetArmFeaturesEnumName(ArmFeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#if !defined(CPU_FEATURES_ARCH_ARM)
+#error "Including cpuinfo_arm.h from a non-arm target."
+#endif
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ int msa : 1; // MIPS SIMD Architecture
+ // https://www.mips.com/products/architectures/ase/simd/
+ int eva : 1; // Enhanced Virtual Addressing
+ // https://www.mips.com/products/architectures/mips64/
+ int r6 : 1; // True if is release 6 of the processor.
+
+ // Make sure to update MipsFeaturesEnum below if you add a field here.
+} MipsFeatures;
+
+typedef struct {
+ MipsFeatures features;
+} MipsInfo;
+
+MipsInfo GetMipsInfo(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+ MIPS_MSA,
+ MIPS_EVA,
+ MIPS_R6,
+ MIPS_LAST_,
+} MipsFeaturesEnum;
+
+int GetMipsFeaturesEnumValue(const MipsFeatures* features,
+ MipsFeaturesEnum value);
+
+const char* GetMipsFeaturesEnumName(MipsFeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#if !defined(CPU_FEATURES_ARCH_MIPS)
+#error "Including cpuinfo_mips.h from a non-mips target."
+#endif
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
--- /dev/null
+// 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.
+
+#ifndef CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+#include "internal/hwcaps.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ int ppc32 : 1;
+ int ppc64 : 1;
+ int ppc601 : 1;
+ int altivec : 1;
+ int fpu : 1;
+ int mmu : 1;
+ int mac_4xx : 1;
+ int unifiedcache : 1;
+ int spe : 1;
+ int efpsingle : 1;
+ int efpdouble : 1;
+ int no_tb : 1;
+ int power4 : 1;
+ int power5 : 1;
+ int power5plus : 1;
+ int cell : 1;
+ int booke : 1;
+ int smt : 1;
+ int icachesnoop : 1;
+ int arch205 : 1;
+ int pa6t : 1;
+ int dfp : 1;
+ int power6ext : 1;
+ int arch206 : 1;
+ int vsx : 1;
+ int pseries_perfmon_compat : 1;
+ int truele : 1;
+ int ppcle : 1;
+ int arch207 : 1;
+ int htm : 1;
+ int dscr : 1;
+ int ebb : 1;
+ int isel : 1;
+ int tar : 1;
+ int vcrypto : 1;
+ int htm_nosc : 1;
+ int arch300 : 1;
+ int ieee128 : 1;
+ int darn : 1;
+ int scv : 1;
+ int htm_no_suspend : 1;
+
+ // Make sure to update PPCFeaturesEnum below if you add a field here.
+} PPCFeatures;
+
+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 model[64]; // 0 terminated string
+ char machine[64]; // 0 terminated string
+ char cpu[64]; // 0 terminated string
+ PlatformType type;
+} PPCPlatformStrings;
+
+PPCPlatformStrings GetPPCPlatformStrings(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+ PPC_32, /* 32 bit mode execution */
+ PPC_64, /* 64 bit mode execution */
+ PPC_601_INSTR, /* Old POWER ISA */
+ PPC_HAS_ALTIVEC, /* SIMD Unit*/
+ PPC_HAS_FPU, /* Floating Point Unit */
+ PPC_HAS_MMU, /* Memory management unit */
+ PPC_HAS_4xxMAC,
+ PPC_UNIFIED_CACHE, /* Unified instruction and data cache */
+ PPC_HAS_SPE, /* Signal processing extention unit */
+ PPC_HAS_EFP_SINGLE, /* SPE single precision fpu */
+ PPC_HAS_EFP_DOUBLE, /* SPE double precision fpu */
+ PPC_NO_TB, /* No timebase */
+ PPC_POWER4,
+ PPC_POWER5,
+ PPC_POWER5_PLUS,
+ PPC_CELL, /* Cell broadband engine */
+ PPC_BOOKE, /* Embedded ISA */
+ PPC_SMT, /* Simultaneous multi-threading */
+ PPC_ICACHE_SNOOP,
+ PPC_ARCH_2_05, /* ISA 2.05 - POWER6 */
+ PPC_PA6T, /* PA Semi 6T core ISA */
+ PPC_HAS_DFP, /* Decimal floating point unit */
+ PPC_POWER6_EXT,
+ PPC_ARCH_2_06, /* ISA 2.06 - POWER7 */
+ PPC_HAS_VSX, /* Vector-scalar extension */
+ PPC_PSERIES_PERFMON_COMPAT, /* Set of backwards compatibile performance
+ monitoring events */
+ PPC_TRUE_LE,
+ PPC_PPC_LE,
+ PPC_ARCH_2_07, /* ISA 2.07 - POWER8 */
+ PPC_HTM, /* Hardware Transactional Memory */
+ PPC_DSCR, /* Data stream control register */
+ PPC_EBB, /* Event base branching */
+ PPC_ISEL, /* Integer select instructions */
+ PPC_TAR, /* Target address register */
+ PPC_VEC_CRYPTO, /* Vector cryptography instructions */
+ PPC_HTM_NOSC, /* Transactions aborted when syscall made*/
+ PPC_ARCH_3_00, /* ISA 3.00 - POWER9 */
+ PPC_HAS_IEEE128, /* VSX IEEE Binary Float 128-bit */
+ PPC_DARN, /* Deliver a random number instruction */
+ PPC_SCV, /* scv syscall */
+ PPC_HTM_NO_SUSPEND, /* TM w/out suspended state */
+ PPC_LAST_,
+} PPCFeaturesEnum;
+
+int GetPPCFeaturesEnumValue(const PPCFeatures* features, PPCFeaturesEnum value);
+
+const char* GetPPCFeaturesEnumName(PPCFeaturesEnum);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#if !defined(CPU_FEATURES_ARCH_PPC)
+#error "Including cpuinfo_ppc.h from a non-ppc target."
+#endif
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
--- /dev/null
+// Copyright 2017 Google LLC
+// Copyright 2020 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
+#define CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
+
+#include "cpu_features_cache_info.h"
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+// 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 {
+ int fpu : 1;
+ int tsc : 1;
+ int cx8 : 1;
+ int clfsh : 1;
+ int mmx : 1;
+ int aes : 1;
+ int erms : 1;
+ int f16c : 1;
+ int fma4 : 1;
+ int fma3 : 1;
+ int vaes : 1;
+ int vpclmulqdq : 1;
+ int bmi1 : 1;
+ int hle : 1;
+ int bmi2 : 1;
+ int rtm : 1;
+ int rdseed : 1;
+ int clflushopt : 1;
+ int clwb : 1;
+
+ int sse : 1;
+ int sse2 : 1;
+ int sse3 : 1;
+ int ssse3 : 1;
+ int sse4_1 : 1;
+ int sse4_2 : 1;
+ int sse4a : 1;
+
+ int avx : 1;
+ int avx2 : 1;
+
+ int avx512f : 1;
+ int avx512cd : 1;
+ int avx512er : 1;
+ int avx512pf : 1;
+ int avx512bw : 1;
+ int avx512dq : 1;
+ int avx512vl : 1;
+ int avx512ifma : 1;
+ int avx512vbmi : 1;
+ int avx512vbmi2 : 1;
+ int avx512vnni : 1;
+ int avx512bitalg : 1;
+ int avx512vpopcntdq : 1;
+ int avx512_4vnniw : 1;
+ int avx512_4vbmi2 : 1;
+ int avx512_second_fma : 1;
+ int avx512_4fmaps : 1;
+ int avx512_bf16 : 1;
+ int avx512_vp2intersect : 1;
+ int amx_bf16 : 1;
+ int amx_tile : 1;
+ int amx_int8 : 1;
+
+ int pclmulqdq : 1;
+ int smx : 1;
+ int sgx : 1;
+ int cx16 : 1; // aka. CMPXCHG16B
+ int sha : 1;
+ int popcnt : 1;
+ int movbe : 1;
+ int rdrnd : 1;
+
+ int dca : 1;
+ int ss : 1;
+ // Make sure to update X86FeaturesEnum below if you add a field here.
+} X86Features;
+
+typedef struct {
+ X86Features features;
+ int family;
+ int model;
+ int stepping;
+ char vendor[13]; // 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
+} X86Microarchitecture;
+
+// Returns the underlying microarchitecture by looking at X86Info's vendor,
+// family and model.
+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.
+void FillX86BrandString(char brand_string[49]);
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+typedef enum {
+ X86_FPU,
+ X86_TSC,
+ X86_CX8,
+ X86_CLFSH,
+ X86_MMX,
+ X86_AES,
+ X86_ERMS,
+ X86_F16C,
+ X86_FMA4,
+ X86_FMA3,
+ X86_VAES,
+ X86_VPCLMULQDQ,
+ X86_BMI1,
+ X86_HLE,
+ X86_BMI2,
+ X86_RTM,
+ X86_RDSEED,
+ X86_CLFLUSHOPT,
+ X86_CLWB,
+ X86_SSE,
+ X86_SSE2,
+ X86_SSE3,
+ X86_SSSE3,
+ X86_SSE4_1,
+ X86_SSE4_2,
+ X86_SSE4A,
+ X86_AVX,
+ X86_AVX2,
+ X86_AVX512F,
+ X86_AVX512CD,
+ X86_AVX512ER,
+ X86_AVX512PF,
+ X86_AVX512BW,
+ X86_AVX512DQ,
+ X86_AVX512VL,
+ X86_AVX512IFMA,
+ X86_AVX512VBMI,
+ X86_AVX512VBMI2,
+ X86_AVX512VNNI,
+ X86_AVX512BITALG,
+ X86_AVX512VPOPCNTDQ,
+ X86_AVX512_4VNNIW,
+ X86_AVX512_4VBMI2,
+ X86_AVX512_SECOND_FMA,
+ X86_AVX512_4FMAPS,
+ X86_AVX512_BF16,
+ X86_AVX512_VP2INTERSECT,
+ X86_AMX_BF16,
+ X86_AMX_TILE,
+ X86_AMX_INT8,
+ X86_PCLMULQDQ,
+ X86_SMX,
+ X86_SGX,
+ X86_CX16,
+ X86_SHA,
+ X86_POPCNT,
+ X86_MOVBE,
+ X86_RDRND,
+ X86_DCA,
+ X86_SS,
+ X86_LAST_,
+} X86FeaturesEnum;
+
+int GetX86FeaturesEnumValue(const X86Features* features, X86FeaturesEnum value);
+
+const char* GetX86FeaturesEnumName(X86FeaturesEnum);
+
+const char* GetX86MicroarchitectureName(X86Microarchitecture);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#if !defined(CPU_FEATURES_ARCH_X86)
+#error "Including cpuinfo_x86.h from a non-x86 target."
+#endif
+
+#endif // CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+inline static bool IsBitSet(uint32_t reg, uint32_t bit) {
+ return (reg >> bit) & 0x1;
+}
+
+inline static uint32_t ExtractBitRange(uint32_t reg, uint32_t msb,
+ uint32_t lsb) {
+ const uint64_t bits = msb - lsb + 1ULL;
+ const uint64_t mask = (1ULL << bits) - 1ULL;
+ assert(msb >= lsb);
+ return (reg >> lsb) & mask;
+}
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
--- /dev/null
+// 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 CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
+
+#include <stdint.h>
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+// A struct to hold the result of a call to cpuid.
+typedef struct {
+ uint32_t eax, ebx, ecx, edx;
+} Leaf;
+
+// Returns the result of a call to the cpuid instruction.
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx);
+
+// Returns the eax value of the XCR0 register.
+uint32_t GetXCR0Eax(void);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
--- /dev/null
+// 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.
+
+// An interface for the filesystem that allows mocking the filesystem in
+// unittests.
+#ifndef CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+// Same as linux "open(filename, O_RDONLY)", retries automatically on EINTR.
+int CpuFeatures_OpenFile(const char* filename);
+
+// Same as linux "read(file_descriptor, buffer, buffer_size)", retries
+// automatically on EINTR.
+int CpuFeatures_ReadFile(int file_descriptor, void* buffer, size_t buffer_size);
+
+// Same as linux "close(file_descriptor)".
+void CpuFeatures_CloseFile(int file_descriptor);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_FILESYSTEM_H_
--- /dev/null
+// 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.
+
+// Interface to retrieve hardware capabilities. It relies on Linux's getauxval
+// or `/proc/self/auxval` under the hood.
+#ifndef CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+// To avoid depending on the linux kernel we reproduce the architecture specific
+// constants here.
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/arm64/include/uapi/asm/hwcap.h
+#define AARCH64_HWCAP_FP (1UL << 0)
+#define AARCH64_HWCAP_ASIMD (1UL << 1)
+#define AARCH64_HWCAP_EVTSTRM (1UL << 2)
+#define AARCH64_HWCAP_AES (1UL << 3)
+#define AARCH64_HWCAP_PMULL (1UL << 4)
+#define AARCH64_HWCAP_SHA1 (1UL << 5)
+#define AARCH64_HWCAP_SHA2 (1UL << 6)
+#define AARCH64_HWCAP_CRC32 (1UL << 7)
+#define AARCH64_HWCAP_ATOMICS (1UL << 8)
+#define AARCH64_HWCAP_FPHP (1UL << 9)
+#define AARCH64_HWCAP_ASIMDHP (1UL << 10)
+#define AARCH64_HWCAP_CPUID (1UL << 11)
+#define AARCH64_HWCAP_ASIMDRDM (1UL << 12)
+#define AARCH64_HWCAP_JSCVT (1UL << 13)
+#define AARCH64_HWCAP_FCMA (1UL << 14)
+#define AARCH64_HWCAP_LRCPC (1UL << 15)
+#define AARCH64_HWCAP_DCPOP (1UL << 16)
+#define AARCH64_HWCAP_SHA3 (1UL << 17)
+#define AARCH64_HWCAP_SM3 (1UL << 18)
+#define AARCH64_HWCAP_SM4 (1UL << 19)
+#define AARCH64_HWCAP_ASIMDDP (1UL << 20)
+#define AARCH64_HWCAP_SHA512 (1UL << 21)
+#define AARCH64_HWCAP_SVE (1UL << 22)
+#define AARCH64_HWCAP_ASIMDFHM (1UL << 23)
+#define AARCH64_HWCAP_DIT (1UL << 24)
+#define AARCH64_HWCAP_USCAT (1UL << 25)
+#define AARCH64_HWCAP_ILRCPC (1UL << 26)
+#define AARCH64_HWCAP_FLAGM (1UL << 27)
+#define AARCH64_HWCAP_SSBS (1UL << 28)
+#define AARCH64_HWCAP_SB (1UL << 29)
+#define AARCH64_HWCAP_PACA (1UL << 30)
+#define AARCH64_HWCAP_PACG (1UL << 31)
+
+#define AARCH64_HWCAP2_DCPODP (1UL << 0)
+#define AARCH64_HWCAP2_SVE2 (1UL << 1)
+#define AARCH64_HWCAP2_SVEAES (1UL << 2)
+#define AARCH64_HWCAP2_SVEPMULL (1UL << 3)
+#define AARCH64_HWCAP2_SVEBITPERM (1UL << 4)
+#define AARCH64_HWCAP2_SVESHA3 (1UL << 5)
+#define AARCH64_HWCAP2_SVESM4 (1UL << 6)
+#define AARCH64_HWCAP2_FLAGM2 (1UL << 7)
+#define AARCH64_HWCAP2_FRINT (1UL << 8)
+#define AARCH64_HWCAP2_SVEI8MM (1UL << 9)
+#define AARCH64_HWCAP2_SVEF32MM (1UL << 10)
+#define AARCH64_HWCAP2_SVEF64MM (1UL << 11)
+#define AARCH64_HWCAP2_SVEBF16 (1UL << 12)
+#define AARCH64_HWCAP2_I8MM (1UL << 13)
+#define AARCH64_HWCAP2_BF16 (1UL << 14)
+#define AARCH64_HWCAP2_DGH (1UL << 15)
+#define AARCH64_HWCAP2_RNG (1UL << 16)
+#define AARCH64_HWCAP2_BTI (1UL << 17)
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/uapi/asm/hwcap.h
+#define ARM_HWCAP_SWP (1UL << 0)
+#define ARM_HWCAP_HALF (1UL << 1)
+#define ARM_HWCAP_THUMB (1UL << 2)
+#define ARM_HWCAP_26BIT (1UL << 3)
+#define ARM_HWCAP_FAST_MULT (1UL << 4)
+#define ARM_HWCAP_FPA (1UL << 5)
+#define ARM_HWCAP_VFP (1UL << 6)
+#define ARM_HWCAP_EDSP (1UL << 7)
+#define ARM_HWCAP_JAVA (1UL << 8)
+#define ARM_HWCAP_IWMMXT (1UL << 9)
+#define ARM_HWCAP_CRUNCH (1UL << 10)
+#define ARM_HWCAP_THUMBEE (1UL << 11)
+#define ARM_HWCAP_NEON (1UL << 12)
+#define ARM_HWCAP_VFPV3 (1UL << 13)
+#define ARM_HWCAP_VFPV3D16 (1UL << 14)
+#define ARM_HWCAP_TLS (1UL << 15)
+#define ARM_HWCAP_VFPV4 (1UL << 16)
+#define ARM_HWCAP_IDIVA (1UL << 17)
+#define ARM_HWCAP_IDIVT (1UL << 18)
+#define ARM_HWCAP_VFPD32 (1UL << 19)
+#define ARM_HWCAP_LPAE (1UL << 20)
+#define ARM_HWCAP_EVTSTRM (1UL << 21)
+#define ARM_HWCAP2_AES (1UL << 0)
+#define ARM_HWCAP2_PMULL (1UL << 1)
+#define ARM_HWCAP2_SHA1 (1UL << 2)
+#define ARM_HWCAP2_SHA2 (1UL << 3)
+#define ARM_HWCAP2_CRC32 (1UL << 4)
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/mips/include/uapi/asm/hwcap.h
+#define MIPS_HWCAP_R6 (1UL << 0)
+#define MIPS_HWCAP_MSA (1UL << 1)
+#define MIPS_HWCAP_CRC32 (1UL << 2)
+
+// http://elixir.free-electrons.com/linux/latest/source/arch/powerpc/include/uapi/asm/cputable.h
+#ifndef _UAPI__ASM_POWERPC_CPUTABLE_H
+/* in AT_HWCAP */
+#define PPC_FEATURE_32 0x80000000
+#define PPC_FEATURE_64 0x40000000
+#define PPC_FEATURE_601_INSTR 0x20000000
+#define PPC_FEATURE_HAS_ALTIVEC 0x10000000
+#define PPC_FEATURE_HAS_FPU 0x08000000
+#define PPC_FEATURE_HAS_MMU 0x04000000
+#define PPC_FEATURE_HAS_4xxMAC 0x02000000
+#define PPC_FEATURE_UNIFIED_CACHE 0x01000000
+#define PPC_FEATURE_HAS_SPE 0x00800000
+#define PPC_FEATURE_HAS_EFP_SINGLE 0x00400000
+#define PPC_FEATURE_HAS_EFP_DOUBLE 0x00200000
+#define PPC_FEATURE_NO_TB 0x00100000
+#define PPC_FEATURE_POWER4 0x00080000
+#define PPC_FEATURE_POWER5 0x00040000
+#define PPC_FEATURE_POWER5_PLUS 0x00020000
+#define PPC_FEATURE_CELL 0x00010000
+#define PPC_FEATURE_BOOKE 0x00008000
+#define PPC_FEATURE_SMT 0x00004000
+#define PPC_FEATURE_ICACHE_SNOOP 0x00002000
+#define PPC_FEATURE_ARCH_2_05 0x00001000
+#define PPC_FEATURE_PA6T 0x00000800
+#define PPC_FEATURE_HAS_DFP 0x00000400
+#define PPC_FEATURE_POWER6_EXT 0x00000200
+#define PPC_FEATURE_ARCH_2_06 0x00000100
+#define PPC_FEATURE_HAS_VSX 0x00000080
+
+#define PPC_FEATURE_PSERIES_PERFMON_COMPAT 0x00000040
+
+/* Reserved - do not use 0x00000004 */
+#define PPC_FEATURE_TRUE_LE 0x00000002
+#define PPC_FEATURE_PPC_LE 0x00000001
+
+/* in AT_HWCAP2 */
+#define PPC_FEATURE2_ARCH_2_07 0x80000000
+#define PPC_FEATURE2_HTM 0x40000000
+#define PPC_FEATURE2_DSCR 0x20000000
+#define PPC_FEATURE2_EBB 0x10000000
+#define PPC_FEATURE2_ISEL 0x08000000
+#define PPC_FEATURE2_TAR 0x04000000
+#define PPC_FEATURE2_VEC_CRYPTO 0x02000000
+#define PPC_FEATURE2_HTM_NOSC 0x01000000
+#define PPC_FEATURE2_ARCH_3_00 0x00800000
+#define PPC_FEATURE2_HAS_IEEE128 0x00400000
+#define PPC_FEATURE2_DARN 0x00200000
+#define PPC_FEATURE2_SCV 0x00100000
+#define PPC_FEATURE2_HTM_NO_SUSPEND 0x00080000
+#endif
+
+typedef struct {
+ unsigned long hwcaps;
+ unsigned long hwcaps2;
+} HardwareCapabilities;
+
+HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
+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);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_HWCAPS_H_
--- /dev/null
+// 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.
+
+// Reads a file line by line and stores the data on the stack. This allows
+// parsing files in one go without allocating.
+#ifndef CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
+
+#include <stdbool.h>
+
+#include "cpu_features_macros.h"
+#include "internal/string_view.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ char buffer[STACK_LINE_READER_BUFFER_SIZE];
+ StringView view;
+ int fd;
+ bool skip_mode;
+} StackLineReader;
+
+// Initializes a StackLineReader.
+void StackLineReader_Initialize(StackLineReader* reader, int fd);
+
+typedef struct {
+ StringView line; // A view of the line.
+ bool eof; // Nothing more to read, we reached EOF.
+ bool full_line; // If false the line was truncated to
+ // STACK_LINE_READER_BUFFER_SIZE.
+} LineResult;
+
+// Reads the file pointed to by fd and tries to read a full line.
+LineResult StackLineReader_NextLine(StackLineReader* reader);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_STACK_LINE_READER_H_
--- /dev/null
+// 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.
+
+// A view over a piece of string. The view is not 0 terminated.
+#ifndef CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
+#define CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "cpu_features_macros.h"
+
+CPU_FEATURES_START_CPP_NAMESPACE
+
+typedef struct {
+ const char* ptr;
+ size_t size;
+} StringView;
+
+#ifdef __cplusplus
+static const StringView kEmptyStringView = {NULL, 0};
+#else
+static const StringView kEmptyStringView;
+#endif
+
+// Returns a StringView from the provided string.
+// Passing NULL is valid only if size is 0.
+static inline StringView view(const char* str, const size_t size) {
+ StringView view;
+ view.ptr = str;
+ view.size = size;
+ return view;
+}
+
+static inline StringView str(const char* str) { return view(str, strlen(str)); }
+
+// Returns the index of the first occurrence of c in view or -1 if not found.
+int CpuFeatures_StringView_IndexOfChar(const StringView view, char c);
+
+// Returns the index of the first occurrence of sub_view in view or -1 if not
+// found.
+int CpuFeatures_StringView_IndexOf(const StringView view,
+ const StringView sub_view);
+
+// Returns whether a is equal to b (same content).
+bool CpuFeatures_StringView_IsEquals(const StringView a, const StringView b);
+
+// Returns whether a starts with b.
+bool CpuFeatures_StringView_StartsWith(const StringView a, const StringView b);
+
+// Removes count characters from the beginning of view or kEmptyStringView if
+// count if greater than view.size.
+StringView CpuFeatures_StringView_PopFront(const StringView str_view,
+ size_t count);
+
+// Removes count characters from the end of view or kEmptyStringView if count if
+// greater than view.size.
+StringView CpuFeatures_StringView_PopBack(const StringView str_view,
+ size_t count);
+
+// Keeps the count first characters of view or view if count if greater than
+// view.size.
+StringView CpuFeatures_StringView_KeepFront(const StringView str_view,
+ size_t count);
+
+// Retrieves the first character of view. If view is empty the behavior is
+// undefined.
+char CpuFeatures_StringView_Front(const StringView view);
+
+// Retrieves the last character of view. If view is empty the behavior is
+// undefined.
+char CpuFeatures_StringView_Back(const StringView view);
+
+// Removes leading and tailing space characters.
+StringView CpuFeatures_StringView_TrimWhitespace(StringView view);
+
+// Convert StringView to positive integer. e.g. "42", "0x2a".
+// Returns -1 on error.
+int CpuFeatures_StringView_ParsePositiveNumber(const StringView view);
+
+// Copies src StringView to dst buffer.
+void CpuFeatures_StringView_CopyString(const StringView src, char* dst,
+ size_t dst_size);
+
+// Checks if line contains the specified whitespace separated word.
+bool CpuFeatures_StringView_HasWord(const StringView line,
+ const char* const word);
+
+// Get key/value from line. key and value are separated by ": ".
+// key and value are cleaned up from leading and trailing whitespaces.
+bool CpuFeatures_StringView_GetAttributeKeyValue(const StringView line,
+ StringView* key,
+ StringView* value);
+
+CPU_FEATURES_END_CPP_NAMESPACE
+
+#endif // CPU_FEATURES_INCLUDE_INTERNAL_STRING_VIEW_H_
--- /dev/null
+
+#
+# library : NDK compat
+#
+find_package(Threads REQUIRED)
+set (NDK_COMPAT_HDRS cpu-features.h)
+set (NDK_COMPAT_SRCS
+ cpu-features.c
+ $<TARGET_OBJECTS:utils>
+ $<TARGET_OBJECTS:unix_based_hardware_detection>
+)
+# Note that following `add_cpu_features_headers_and_sources` will use
+# NDK_COMPAT_SRCS in lieu of NDK_COMPAT_HDRS because we don't want cpu_features
+# headers to be installed alongside ndk_compat.
+add_cpu_features_headers_and_sources(NDK_COMPAT_SRCS NDK_COMPAT_SRCS)
+add_library(ndk_compat ${NDK_COMPAT_HDRS} ${NDK_COMPAT_SRCS})
+setup_include_and_definitions(ndk_compat)
+target_include_directories(ndk_compat PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
+target_link_libraries(ndk_compat PUBLIC ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
+set_target_properties(ndk_compat PROPERTIES PUBLIC_HEADER "${NDK_COMPAT_HDRS}")
+
+include(GNUInstallDirs)
+install(TARGETS ndk_compat
+ EXPORT CpuFeaturesNdkCompatTargets
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ndk_compat
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+install(EXPORT CpuFeaturesNdkCompatTargets
+ NAMESPACE CpuFeatures::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeaturesNdkCompat
+ COMPONENT Devel
+)
+include(CMakePackageConfigHelpers)
+configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/CpuFeaturesNdkCompatConfig.cmake.in
+ "${PROJECT_BINARY_DIR}/CpuFeaturesNdkCompatConfig.cmake"
+ INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeaturesNdkCompat"
+ NO_SET_AND_CHECK_MACRO
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+)
+write_basic_package_version_file(
+ "${PROJECT_BINARY_DIR}/CpuFeaturesNdkCompatConfigVersion.cmake"
+ COMPATIBILITY SameMajorVersion
+)
+install(
+ FILES
+ "${PROJECT_BINARY_DIR}/CpuFeaturesNdkCompatConfig.cmake"
+ "${PROJECT_BINARY_DIR}/CpuFeaturesNdkCompatConfigVersion.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeaturesNdkCompat"
+ COMPONENT Devel
+)
+
+#
+# program : NDK compat test program
+#
+if(ENABLE_TESTING)
+ add_executable(ndk-compat-test ndk-compat-test.c)
+ target_link_libraries(ndk-compat-test PRIVATE ndk_compat)
+endif()
--- /dev/null
+Provides a header compatible with [android's NDK cpu-features.h](https://android.googlesource.com/platform/ndk/+/master/sources/android/cpufeatures/cpu-features.h).
+
+It is intended to be a drop in replacement for this header and help users
+transition from the NDK to [Google's cpu_features library](https://github.com/google/cpu_features).
--- /dev/null
+#include "cpu-features.h"
+
+#include <pthread.h>
+
+#include "cpu_features_macros.h"
+#include "internal/filesystem.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+#if defined(CPU_FEATURES_ARCH_ARM)
+#include "cpuinfo_arm.h"
+#elif defined(CPU_FEATURES_ARCH_X86)
+#include "cpuinfo_x86.h"
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+#include "cpuinfo_mips.h"
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+#include "cpuinfo_aarch64.h"
+#endif
+
+static pthread_once_t g_once;
+static int g_inited;
+static uint64_t g_cpuFeatures;
+static int g_cpuCount;
+
+#ifdef CPU_FEATURES_ARCH_ARM
+static uint32_t g_cpuIdArm;
+#endif
+
+static void set_cpu_mask_bit(uint32_t index, uint32_t* cpu_mask) {
+ *cpu_mask |= 1UL << index;
+}
+
+// Examples of valid inputs: "31", "4-31"
+static void parse_cpu_mask(const StringView text, uint32_t* cpu_mask) {
+ int separator_index = CpuFeatures_StringView_IndexOfChar(text, '-');
+ if (separator_index < 0) { // A single cpu index
+ int cpu_index = CpuFeatures_StringView_ParsePositiveNumber(text);
+ if (cpu_index < 0) return;
+ set_cpu_mask_bit(cpu_index, cpu_mask);
+ } else {
+ int cpu_index_a = CpuFeatures_StringView_ParsePositiveNumber(
+ CpuFeatures_StringView_KeepFront(text, separator_index));
+ int cpu_index_b = CpuFeatures_StringView_ParsePositiveNumber(
+ CpuFeatures_StringView_PopFront(text, separator_index + 1));
+ int i;
+ if (cpu_index_a < 0 || cpu_index_b < 0) return;
+ for (i = cpu_index_a; i <= cpu_index_b; ++i) {
+ if (i < 32) {
+ set_cpu_mask_bit(i, cpu_mask);
+ }
+ }
+ }
+}
+
+// Format specification from
+// https://www.kernel.org/doc/Documentation/cputopology.txt
+// Examples of valid inputs: "31", "2,4-31,32-63", "0-1,3"
+static void parse_cpu_mask_line(const LineResult result, uint32_t* cpu_mask) {
+ if (!result.full_line || result.eof) return;
+ StringView line = result.line;
+ for (; line.size > 0;) {
+ int next_entry_index = CpuFeatures_StringView_IndexOfChar(line, ',');
+ if (next_entry_index < 0) {
+ parse_cpu_mask(line, cpu_mask);
+ break;
+ }
+ StringView entry = CpuFeatures_StringView_KeepFront(line, next_entry_index);
+ parse_cpu_mask(entry, cpu_mask);
+ line = CpuFeatures_StringView_PopFront(line, next_entry_index + 1);
+ }
+}
+
+static void update_cpu_mask_from_file(const char* filename,
+ uint32_t* cpu_mask) {
+ const int fd = CpuFeatures_OpenFile(filename);
+ if (fd >= 0) {
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, fd);
+ parse_cpu_mask_line(StackLineReader_NextLine(&reader), cpu_mask);
+ CpuFeatures_CloseFile(fd);
+ }
+}
+
+static int get_cpu_count(void) {
+ uint32_t cpu_mask = 0;
+ update_cpu_mask_from_file("/sys/devices/system/cpu/present", &cpu_mask);
+ update_cpu_mask_from_file("/sys/devices/system/cpu/possible", &cpu_mask);
+ return __builtin_popcount(cpu_mask);
+}
+
+static void android_cpuInit(void) {
+ g_cpuFeatures = 0;
+ g_cpuCount = 1;
+ g_inited = 1;
+
+ g_cpuCount = get_cpu_count();
+ if (g_cpuCount == 0) {
+ g_cpuCount = 1;
+ }
+#if defined(CPU_FEATURES_ARCH_ARM)
+ ArmInfo info = GetArmInfo();
+ if (info.architecture == 7) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
+ if (info.features.vfpv3) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+ if (info.features.neon) {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON;
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFP_D32;
+ }
+ if (info.features.vfpv3d16) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFP_FP16;
+ if (info.features.idiva) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
+ if (info.features.idivt) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2;
+ if (info.features.iwmmxt) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt;
+ if (info.features.aes) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES;
+ if (info.features.pmull) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL;
+ if (info.features.sha1) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1;
+ if (info.features.sha2) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2;
+ if (info.features.crc32) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32;
+ if (info.architecture >= 6)
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
+ if (info.features.vfp) g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2;
+ if (info.features.vfpv4) {
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFP_FMA;
+ g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA;
+ }
+ g_cpuIdArm = GetArmCpuId(&info);
+#elif defined(CPU_FEATURES_ARCH_X86)
+ X86Info info = GetX86Info();
+ if (info.features.ssse3) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3;
+ if (info.features.popcnt) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
+ if (info.features.movbe) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
+ if (info.features.sse4_1) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1;
+ if (info.features.sse4_2) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2;
+ if (info.features.aes) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI;
+ if (info.features.avx) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX;
+ if (info.features.rdrnd) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND;
+ if (info.features.avx2) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2;
+ if (info.features.sha) g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI;
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+ MipsInfo info = GetMipsInfo();
+ if (info.features.r6) g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6;
+ if (info.features.msa) g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA;
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+ Aarch64Info info = GetAarch64Info();
+ if (info.features.fp) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP;
+ if (info.features.asimd) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD;
+ if (info.features.aes) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES;
+ if (info.features.pmull) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL;
+ if (info.features.sha1) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1;
+ if (info.features.sha2) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2;
+ if (info.features.crc32) g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32;
+#endif
+}
+
+AndroidCpuFamily android_getCpuFamily(void) {
+#if defined(CPU_FEATURES_ARCH_ARM)
+ return ANDROID_CPU_FAMILY_ARM;
+#elif defined(CPU_FEATURES_ARCH_X86_32)
+ return ANDROID_CPU_FAMILY_X86;
+#elif defined(CPU_FEATURES_ARCH_MIPS64)
+ return ANDROID_CPU_FAMILY_MIPS64;
+#elif defined(CPU_FEATURES_ARCH_MIPS32)
+ return ANDROID_CPU_FAMILY_MIPS;
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+ return ANDROID_CPU_FAMILY_ARM64;
+#elif defined(CPU_FEATURES_ARCH_X86_64)
+ return ANDROID_CPU_FAMILY_X86_64;
+#else
+ return ANDROID_CPU_FAMILY_UNKNOWN;
+#endif
+}
+
+uint64_t android_getCpuFeatures(void) {
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuFeatures;
+}
+
+int android_getCpuCount(void) {
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuCount;
+}
+
+static void android_cpuInitDummy(void) { g_inited = 1; }
+
+int android_setCpu(int cpu_count, uint64_t cpu_features) {
+ /* Fail if the library was already initialized. */
+ if (g_inited) return 0;
+ g_cpuCount = (cpu_count <= 0 ? 1 : cpu_count);
+ g_cpuFeatures = cpu_features;
+ pthread_once(&g_once, android_cpuInitDummy);
+ return 1;
+}
+
+#ifdef CPU_FEATURES_ARCH_ARM
+
+uint32_t android_getCpuIdArm(void) {
+ pthread_once(&g_once, android_cpuInit);
+ return g_cpuIdArm;
+}
+
+int android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id) {
+ if (!android_setCpu(cpu_count, cpu_features)) return 0;
+ g_cpuIdArm = cpu_id;
+ return 1;
+}
+
+#endif // CPU_FEATURES_ARCH_ARM
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef GOOGLE_CPU_FEATURES_H
+#define GOOGLE_CPU_FEATURES_H
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* A list of valid values returned by android_getCpuFamily().
+ * They describe the CPU Architecture of the current process.
+ */
+typedef enum {
+ ANDROID_CPU_FAMILY_UNKNOWN = 0,
+ ANDROID_CPU_FAMILY_ARM,
+ ANDROID_CPU_FAMILY_X86,
+ ANDROID_CPU_FAMILY_MIPS,
+ ANDROID_CPU_FAMILY_ARM64,
+ ANDROID_CPU_FAMILY_X86_64,
+ ANDROID_CPU_FAMILY_MIPS64,
+ ANDROID_CPU_FAMILY_MAX /* do not remove */
+} AndroidCpuFamily;
+
+/* Return the CPU family of the current process.
+ *
+ * Note that this matches the bitness of the current process. I.e. when
+ * running a 32-bit binary on a 64-bit capable CPU, this will return the
+ * 32-bit CPU family value.
+ */
+extern AndroidCpuFamily android_getCpuFamily(void);
+
+/* Return a bitmap describing a set of optional CPU features that are
+ * supported by the current device's CPU. The exact bit-flags returned
+ * depend on the value returned by android_getCpuFamily(). See the
+ * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details.
+ */
+extern uint64_t android_getCpuFeatures(void);
+
+/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be
+ * recognized by the library (see note below for 64-bit ARM). Value details
+ * are:
+ *
+ * VFPv2:
+ * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs
+ * support these instructions. VFPv2 is a subset of VFPv3 so this will
+ * be set whenever VFPv3 is set too.
+ *
+ * ARMv7:
+ * CPU supports the ARMv7-A basic instruction set.
+ * This feature is mandated by the 'armeabi-v7a' ABI.
+ *
+ * VFPv3:
+ * CPU supports the VFPv3-D16 instruction set, providing hardware FPU
+ * support for single and double precision floating point registers.
+ * Note that only 16 FPU registers are available by default, unless
+ * the D32 bit is set too. This feature is also mandated by the
+ * 'armeabi-v7a' ABI.
+ *
+ * VFP_D32:
+ * CPU VFP optional extension that provides 32 FPU registers,
+ * instead of 16. Note that ARM mandates this feature is the 'NEON'
+ * feature is implemented by the CPU.
+ *
+ * NEON:
+ * CPU FPU supports "ARM Advanced SIMD" instructions, also known as
+ * NEON. Note that this mandates the VFP_D32 feature as well, per the
+ * ARM Architecture specification.
+ *
+ * VFP_FP16:
+ * Half-width floating precision VFP extension. If set, the CPU
+ * supports instructions to perform floating-point operations on
+ * 16-bit registers. This is part of the VFPv4 specification, but
+ * not mandated by any Android ABI.
+ *
+ * VFP_FMA:
+ * Fused multiply-accumulate VFP instructions extension. Also part of
+ * the VFPv4 specification, but not mandated by any Android ABI.
+ *
+ * NEON_FMA:
+ * Fused multiply-accumulate NEON instructions extension. Optional
+ * extension from the VFPv4 specification, but not mandated by any
+ * Android ABI.
+ *
+ * IDIV_ARM:
+ * Integer division available in ARM mode. Only available
+ * on recent CPUs (e.g. Cortex-A15).
+ *
+ * IDIV_THUMB2:
+ * Integer division available in Thumb-2 mode. Only available
+ * on recent CPUs (e.g. Cortex-A15).
+ *
+ * iWMMXt:
+ * Optional extension that adds MMX registers and operations to an
+ * ARM CPU. This is only available on a few XScale-based CPU designs
+ * sold by Marvell. Pretty rare in practice.
+ *
+ * AES:
+ * CPU supports AES instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * CRC32:
+ * CPU supports CRC32 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * SHA2:
+ * CPU supports SHA2 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * SHA1:
+ * CPU supports SHA1 instructions. These instructions are only
+ * available for 32-bit applications running on ARMv8 CPU.
+ *
+ * PMULL:
+ * CPU supports 64-bit PMULL and PMULL2 instructions. These
+ * instructions are only available for 32-bit applications
+ * running on ARMv8 CPU.
+ *
+ * If you want to tell the compiler to generate code that targets one of
+ * the feature set above, you should probably use one of the following
+ * flags (for more details, see technical note at the end of this file):
+ *
+ * -mfpu=vfp
+ * -mfpu=vfpv2
+ * These are equivalent and tell GCC to use VFPv2 instructions for
+ * floating-point operations. Use this if you want your code to
+ * run on *some* ARMv6 devices, and any ARMv7-A device supported
+ * by Android.
+ *
+ * Generated code requires VFPv2 feature.
+ *
+ * -mfpu=vfpv3-d16
+ * Tell GCC to use VFPv3 instructions (using only 16 FPU registers).
+ * This should be generic code that runs on any CPU that supports the
+ * 'armeabi-v7a' Android ABI. Note that no ARMv6 CPU supports this.
+ *
+ * Generated code requires VFPv3 feature.
+ *
+ * -mfpu=vfpv3
+ * Tell GCC to use VFPv3 instructions with 32 FPU registers.
+ * Generated code requires VFPv3|VFP_D32 features.
+ *
+ * -mfpu=neon
+ * Tell GCC to use VFPv3 instructions with 32 FPU registers, and
+ * also support NEON intrinsics (see <arm_neon.h>).
+ * Generated code requires VFPv3|VFP_D32|NEON features.
+ *
+ * -mfpu=vfpv4-d16
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA features.
+ *
+ * -mfpu=vfpv4
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32 features.
+ *
+ * -mfpu=neon-vfpv4
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|NEON|NEON_FMA
+ * features.
+ *
+ * -mcpu=cortex-a7
+ * -mcpu=cortex-a15
+ * Generated code requires VFPv3|VFP_FP16|VFP_FMA|VFP_D32|
+ * NEON|NEON_FMA|IDIV_ARM|IDIV_THUMB2
+ * This flag implies -mfpu=neon-vfpv4.
+ *
+ * -mcpu=iwmmxt
+ * Allows the use of iWMMXt instrinsics with GCC.
+ *
+ * IMPORTANT NOTE: These flags should only be tested when
+ * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a
+ * 32-bit process.
+ *
+ * When running a 64-bit ARM process on an ARMv8 CPU,
+ * android_getCpuFeatures() will return a different set of bitflags
+ */
+enum {
+ ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
+ ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1),
+ ANDROID_CPU_ARM_FEATURE_NEON = (1 << 2),
+ ANDROID_CPU_ARM_FEATURE_LDREX_STREX = (1 << 3),
+ ANDROID_CPU_ARM_FEATURE_VFPv2 = (1 << 4),
+ ANDROID_CPU_ARM_FEATURE_VFP_D32 = (1 << 5),
+ ANDROID_CPU_ARM_FEATURE_VFP_FP16 = (1 << 6),
+ ANDROID_CPU_ARM_FEATURE_VFP_FMA = (1 << 7),
+ ANDROID_CPU_ARM_FEATURE_NEON_FMA = (1 << 8),
+ ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9),
+ ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10),
+ ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11),
+ ANDROID_CPU_ARM_FEATURE_AES = (1 << 12),
+ ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13),
+ ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14),
+ ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15),
+ ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details
+ * are:
+ *
+ * FP:
+ * CPU has Floating-point unit.
+ *
+ * ASIMD:
+ * CPU has Advanced SIMD unit.
+ *
+ * AES:
+ * CPU supports AES instructions.
+ *
+ * CRC32:
+ * CPU supports CRC32 instructions.
+ *
+ * SHA2:
+ * CPU supports SHA2 instructions.
+ *
+ * SHA1:
+ * CPU supports SHA1 instructions.
+ *
+ * PMULL:
+ * CPU supports 64-bit PMULL and PMULL2 instructions.
+ */
+enum {
+ ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0),
+ ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1),
+ ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2),
+ ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3),
+ ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4),
+ ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5),
+ ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86 or
+ * ANDROID_CPU_FAMILY_X86_64.
+ */
+enum {
+ ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0),
+ ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1),
+ ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2),
+ ANDROID_CPU_X86_FEATURE_SSE4_1 = (1 << 3),
+ ANDROID_CPU_X86_FEATURE_SSE4_2 = (1 << 4),
+ ANDROID_CPU_X86_FEATURE_AES_NI = (1 << 5),
+ ANDROID_CPU_X86_FEATURE_AVX = (1 << 6),
+ ANDROID_CPU_X86_FEATURE_RDRAND = (1 << 7),
+ ANDROID_CPU_X86_FEATURE_AVX2 = (1 << 8),
+ ANDROID_CPU_X86_FEATURE_SHA_NI = (1 << 9),
+};
+
+/* The bit flags corresponding to the output of android_getCpuFeatures()
+ * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_MIPS
+ * or ANDROID_CPU_FAMILY_MIPS64. Values are:
+ *
+ * R6:
+ * CPU executes MIPS Release 6 instructions natively, and
+ * supports obsoleted R1..R5 instructions only via kernel traps.
+ *
+ * MSA:
+ * CPU supports Mips SIMD Architecture instructions.
+ */
+enum {
+ ANDROID_CPU_MIPS_FEATURE_R6 = (1 << 0),
+ ANDROID_CPU_MIPS_FEATURE_MSA = (1 << 1),
+};
+
+/* Return the number of CPU cores detected on this device.
+ * Please note the current implementation supports up to 32 cpus.
+ */
+extern int android_getCpuCount(void);
+
+/* The following is used to force the CPU count and features
+ * mask in sandboxed processes. Under 4.1 and higher, these processes
+ * cannot access /proc, which is the only way to get information from
+ * the kernel about the current hardware (at least on ARM).
+ *
+ * It _must_ be called only once, and before any android_getCpuXXX
+ * function, any other case will fail.
+ *
+ * This function return 1 on success, and 0 on failure.
+ */
+extern int android_setCpu(int cpu_count, uint64_t cpu_features);
+
+#ifdef __arm__
+
+/* Retrieve the ARM 32-bit CPUID value from the kernel.
+ * Note that this cannot work on sandboxed processes under 4.1 and
+ * higher, unless you called android_setCpuArm() before.
+ */
+extern uint32_t android_getCpuIdArm(void);
+
+/* An ARM-specific variant of android_setCpu() that also allows you
+ * to set the ARM CPUID field.
+ */
+extern int android_setCpuArm(int cpu_count, uint64_t cpu_features,
+ uint32_t cpu_id);
+
+#endif
+
+__END_DECLS
+#endif /* GOOGLE_CPU_FEATURES_H */
--- /dev/null
+#include <stdio.h>
+
+#include "cpu-features.h"
+
+int main() {
+ printf("android_getCpuFamily()=%d\n", android_getCpuFamily());
+ printf("android_getCpuFeatures()=0x%08llx\n", android_getCpuFeatures());
+ printf("android_getCpuCount()=%d\n", android_getCpuCount());
+#ifdef __arm__
+ printf("android_getCpuIdArm()=0x%04x\n", android_getCpuIdArm());
+#endif //__arm__
+}
--- /dev/null
+#!/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"
+
+function extract() {
+ 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'..."
+ 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}"
+ if [[ ! -d "${DESTINATION}" ]] ; then
+ local ARCHIVE_NAME=$(echo ${URL} | sed 's/.*\///')
+ test -f "${ARCHIVE_NAME}" || wget -q "${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')}
+
+ if echo "${VERSION} ${TARGETS}" | cmp --silent ${QEMU_INSTALL}/.build -; then
+ echo "qemu ${VERSION} up to date!"
+ return 0
+ fi
+
+ echo "VERSION: ${VERSION}"
+ echo "TARGETS: ${TARGETS}"
+
+ 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
+
+ ./configure \
+ --prefix="${QEMU_INSTALL}" \
+ --target-list="${TARGETS}" \
+ --disable-docs \
+ --disable-sdl \
+ --disable-gtk \
+ --disable-gnutls \
+ --disable-gcrypt \
+ --disable-nettle \
+ --disable-curses \
+ --static
+
+ make -j4
+ make install
+
+ echo "$VERSION $TARGETS" > ${QEMU_INSTALL}/.build
+}
+
+function assert_defined(){
+ local VALUE=${1}
+ : "${VALUE?"${1} needs to be defined"}"
+}
+
+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 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)
+}
+
+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}
+ local MIPS_FLAGS=""
+ local LIBC_FOLDER_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;;
+ esac
+
+ 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
+ fi
+
+ 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)
+}
+
+function expand_environment_and_integrate() {
+ assert_defined PROJECT_FOLDER
+ assert_defined TARGET
+
+ BUILD_DIR="${PROJECT_FOLDER}/cmake_build/${TARGET}"
+ mkdir -p "${BUILD_DIR}"
+
+ declare -a CONFIG_NAMES=()
+ declare -a QEMU_ARGS=()
+ 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;;
+ esac
+ integrate
+}
+
+if [ "${CONTINUOUS_INTEGRATION}" = "true" ]; then
+ QEMU_ARCHES=${QEMU_ARCH}
+ expand_environment_and_integrate
+fi
--- /dev/null
+#!/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
+}
+
+# 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
+}
+
+# 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
+}
+
+# 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
+}
+
+# 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
+}
+
+# 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
+}
+
+# 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
+}
+
+function set_mips32() {
+ TOOLCHAIN=CODESCAPE
+ TARGET=mips32
+ QEMU_ARCH=mips
+}
+
+function set_mips32el() {
+ TOOLCHAIN=CODESCAPE
+ TARGET=mips32el
+ QEMU_ARCH=mipsel
+}
+
+function set_mips64() {
+ TOOLCHAIN=CODESCAPE
+ TARGET=mips64
+ QEMU_ARCH=mips64
+}
+
+function set_mips64el() {
+ TOOLCHAIN=CODESCAPE
+ TARGET=mips64el
+ QEMU_ARCH=mips64el
+}
+
+function set_native() {
+ TOOLCHAIN=NATIVE
+ TARGET=native
+ QEMU_ARCH=""
+}
+
+ENVIRONMENTS="
+ set_aarch64-linux-gnu
+ set_arm-linux-gnueabihf
+ set_armv8l-linux-gnueabihf
+ set_arm-linux-gnueabi
+ set_aarch64_be-linux-gnu
+ set_armeb-linux-gnueabihf
+ set_armeb-linux-gnueabi
+ set_mips32
+ set_mips32el
+ set_mips64
+ set_mips64el
+ set_native
+"
+
+set -e
+
+CMAKE_GENERATOR="Ninja"
+
+for SET_ENVIRONMENT in ${ENVIRONMENTS}; do
+ ${SET_ENVIRONMENT}
+ expand_environment_and_integrate
+done
--- /dev/null
+// 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];
+}
--- /dev/null
+// 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_arm.h"
+
+#include <assert.h>
+#include <ctype.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;
+} ProcCpuInfoData;
+
+static int IndexOfNonDigit(StringView str) {
+ size_t index = 0;
+ while (str.size && isdigit(CpuFeatures_StringView_Front(str))) {
+ str = CpuFeatures_StringView_PopFront(str, 1);
+ ++index;
+ }
+ return index;
+}
+
+static bool HandleArmLine(const LineResult result, ArmInfo* const info,
+ ProcCpuInfoData* const proc_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 < ARM_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);
+ } else if (CpuFeatures_StringView_IsEquals(key, str("CPU architecture"))) {
+ // CPU architecture is a number that may be followed by letters. e.g.
+ // "6TEJ", "7".
+ const StringView digits =
+ CpuFeatures_StringView_KeepFront(value, IndexOfNonDigit(value));
+ info->architecture = CpuFeatures_StringView_ParsePositiveNumber(digits);
+ } else if (CpuFeatures_StringView_IsEquals(key, str("Processor")) ||
+ CpuFeatures_StringView_IsEquals(key, str("model name"))) {
+ // Android reports this in a non-Linux standard "Processor" but sometimes
+ // also in "model name", Linux reports it only in "model name"
+ // see RaspberryPiZero (Linux) vs InvalidArmv7 (Android) test-cases
+ proc_info->processor_reports_armv6 =
+ CpuFeatures_StringView_IndexOf(value, str("(v6l)")) >= 0;
+ } else if (CpuFeatures_StringView_IsEquals(key, str("Hardware"))) {
+ proc_info->hardware_reports_goldfish =
+ CpuFeatures_StringView_IsEquals(value, str("Goldfish"));
+ }
+ }
+ return !result.eof;
+}
+
+uint32_t GetArmCpuId(const ArmInfo* const info) {
+ return (ExtractBitRange(info->implementer, 7, 0) << 24) |
+ (ExtractBitRange(info->variant, 3, 0) << 20) |
+ (ExtractBitRange(info->part, 11, 0) << 4) |
+ (ExtractBitRange(info->revision, 3, 0) << 0);
+}
+
+static void FixErrors(ArmInfo* const info,
+ ProcCpuInfoData* const proc_cpu_info_data) {
+ // Fixing Samsung kernel reporting invalid cpu architecture.
+ // http://code.google.com/p/android/issues/detail?id=10812
+ if (proc_cpu_info_data->processor_reports_armv6 && info->architecture >= 7) {
+ info->architecture = 6;
+ }
+
+ // Handle kernel configuration bugs that prevent the correct reporting of CPU
+ // features.
+ switch (GetArmCpuId(info)) {
+ case 0x4100C080:
+ // Special case: 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. Note that it
+ // could also support Thumb IDIV in the future, and this will have to be
+ // slightly updated.
+ if (info->architecture >= 7 &&
+ proc_cpu_info_data->hardware_reports_goldfish) {
+ info->features.idiva = true;
+ }
+ break;
+ case 0x511004D0:
+ // 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;
+ }
+
+ // Propagate cpu features.
+ if (info->features.vfpv4) info->features.vfpv3 = true;
+ if (info->features.neon) info->features.vfpv3 = true;
+ if (info->features.vfpv3) info->features.vfp = true;
+}
+
+static void FillProcCpuInfoData(ArmInfo* const info,
+ ProcCpuInfoData* proc_cpu_info_data) {
+ const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+ if (fd >= 0) {
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, fd);
+ for (;;) {
+ if (!HandleArmLine(StackLineReader_NextLine(&reader), info,
+ proc_cpu_info_data)) {
+ break;
+ }
+ }
+ CpuFeatures_CloseFile(fd);
+ }
+}
+
+static const ArmInfo kEmptyArmInfo;
+
+static const ProcCpuInfoData kEmptyProcCpuInfoData;
+
+ArmInfo GetArmInfo(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).
+ ArmInfo info = kEmptyArmInfo;
+ ProcCpuInfoData proc_cpu_info_data = kEmptyProcCpuInfoData;
+
+ FillProcCpuInfoData(&info, &proc_cpu_info_data);
+ const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
+ for (size_t i = 0; i < ARM_LAST_; ++i) {
+ if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
+ kSetters[i](&info.features, true);
+ }
+ }
+
+ FixErrors(&info, &proc_cpu_info_data);
+
+ 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];
+}
--- /dev/null
+// 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_mips.h"
+
+#include <assert.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(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;
+ // See tests for an example.
+ 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]));
+ }
+ }
+ }
+ return !result.eof;
+}
+
+static void FillProcCpuInfoData(MipsFeatures* const features) {
+ const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+ if (fd >= 0) {
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, fd);
+ for (;;) {
+ if (!HandleMipsLine(StackLineReader_NextLine(&reader), features)) {
+ break;
+ }
+ }
+ CpuFeatures_CloseFile(fd);
+ }
+}
+
+static const MipsInfo kEmptyMipsInfo;
+
+MipsInfo GetMipsInfo(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).
+ MipsInfo info = kEmptyMipsInfo;
+
+ FillProcCpuInfoData(&info.features);
+ const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
+ for (size_t i = 0; i < MIPS_LAST_; ++i) {
+ if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
+ kSetters[i](&info.features, true);
+ }
+ }
+ 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];
+}
--- /dev/null
+// 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];
+}
--- /dev/null
+// Copyright 2017 Google LLC
+// Copyright 2020 Intel Corporation
+//
+// 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_x86.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "internal/bit_utils.h"
+#include "internal/cpuid_x86.h"
+
+#if !defined(CPU_FEATURES_ARCH_X86)
+#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.
+////////////////////////////////////////////////////////////////////////////////
+
+#if defined(CPU_FEATURES_MOCK_CPUID_X86)
+// Implementation will be provided by test/cpuinfo_x86_test.cc.
+#elif defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
+
+#include <cpuid.h>
+
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
+ Leaf leaf;
+ __cpuid_count(leaf_id, ecx, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
+ return leaf;
+}
+
+uint32_t GetXCR0Eax(void) {
+ uint32_t eax, edx;
+ /* named form of xgetbv not supported on OSX, so must use byte form, see:
+ https://github.com/asmjit/asmjit/issues/78
+ */
+ __asm(".byte 0x0F, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0));
+ return eax;
+}
+
+#elif defined(CPU_FEATURES_COMPILER_MSC)
+
+#include <immintrin.h>
+#include <intrin.h> // For __cpuidex()
+
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
+ Leaf leaf;
+ int data[4];
+ __cpuidex(data, leaf_id, ecx);
+ leaf.eax = data[0];
+ leaf.ebx = data[1];
+ leaf.ecx = data[2];
+ leaf.edx = data[3];
+ return leaf;
+}
+
+uint32_t GetXCR0Eax(void) { return (uint32_t)_xgetbv(0); }
+
+#else
+#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) {
+ if (leaf_id <= max_cpuid_leaf) {
+ return GetCpuidLeaf(leaf_id, ecx);
+ } else {
+ return kEmptyLeaf;
+ }
+}
+
+static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
+ return SafeCpuIdEx(max_cpuid_leaf, leaf_id, 0);
+}
+
+#define MASK_XMM 0x2
+#define MASK_YMM 0x4
+#define MASK_MASKREG 0x20
+#define MASK_ZMM0_15 0x40
+#define MASK_ZMM16_31 0x80
+#define MASK_XTILECFG 0x20000
+#define MASK_XTILEDATA 0x40000
+
+static bool HasMask(uint32_t value, uint32_t mask) {
+ return (value & mask) == mask;
+}
+
+// Checks that operating system saves and restores xmm registers during context
+// switches.
+static bool HasXmmOsXSave(uint32_t xcr0_eax) {
+ return HasMask(xcr0_eax, MASK_XMM);
+}
+
+// Checks that operating system saves and restores ymm registers during context
+// switches.
+static bool HasYmmOsXSave(uint32_t xcr0_eax) {
+ return HasMask(xcr0_eax, MASK_XMM | MASK_YMM);
+}
+
+// Checks that operating system saves and restores zmm registers during context
+// switches.
+static bool HasZmmOsXSave(uint32_t xcr0_eax) {
+ return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
+ MASK_ZMM16_31);
+}
+
+// Checks that operating system saves and restores AMX/TMUL state during context
+// switches.
+static bool HasTmmOsXSave(uint32_t xcr0_eax) {
+ return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
+ MASK_ZMM16_31 | MASK_XTILECFG | MASK_XTILEDATA);
+}
+
+static bool HasSecondFMA(uint32_t model) {
+ // Skylake server
+ if (model == 0x55) {
+ char proc_name[49] = {0};
+ FillX86BrandString(proc_name);
+ // detect Xeon
+ if (proc_name[9] == 'X') {
+ // detect Silver or Bronze
+ if (proc_name[17] == 'S' || proc_name[17] == 'B') return false;
+ // detect Gold 5_20 and below, except for Gold 53__
+ if (proc_name[17] == 'G' && proc_name[22] == '5')
+ return ((proc_name[23] == '3') ||
+ (proc_name[24] == '2' && proc_name[25] == '2'));
+ // detect Xeon W 210x
+ if (proc_name[17] == 'W' && proc_name[21] == '0') return false;
+ // detect Xeon D 2xxx
+ if (proc_name[17] == 'D' && proc_name[19] == '2' && proc_name[20] == '1')
+ return false;
+ }
+ return true;
+ }
+ // Cannon Lake client
+ if (model == 0x66) return false;
+ // Ice Lake client
+ if (model == 0x7d || model == 0x7e) return false;
+ // This is the right default...
+ return true;
+}
+
+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 const CacheLevelInfo kEmptyCacheLevelInfo;
+
+static CacheLevelInfo GetCacheLevelInfo(const uint32_t reg) {
+ const int UNDEF = -1;
+ const int KiB = 1024;
+ const int MiB = 1024 * KiB;
+ switch (reg) {
+ case 0x01:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 32,
+ .partitioning = 0};
+ case 0x02:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * MiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 2,
+ .partitioning = 0};
+ case 0x03:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0x04:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 8,
+ .partitioning = 0};
+ case 0x05:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 32,
+ .partitioning = 0};
+ case 0x06:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 8 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x08:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 16 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x09:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 32 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x0A:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 8 * KiB,
+ .ways = 2,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x0B:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 4,
+ .partitioning = 0};
+ case 0x0C:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 16 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x0D:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 16 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x0E:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 24 * KiB,
+ .ways = 6,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x1D:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 128 * KiB,
+ .ways = 2,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x21:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 256 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x22:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x23:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x24:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x25:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x29:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x2C:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 32 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x30:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 32 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x40:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = UNDEF,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x41:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 128 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x42:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 256 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x43:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x44:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x45:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x46:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x47:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 8 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x48:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 3 * MiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x49:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case (0x49 | (1 << 8)):
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4A:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 6 * MiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4B:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 8 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4C:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 12 * MiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4D:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 16 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4E:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 6 * MiB,
+ .ways = 24,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x4F:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 32,
+ .partitioning = 0};
+ case 0x50:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0x51:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 128,
+ .partitioning = 0};
+ case 0x52:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 256,
+ .partitioning = 0};
+ case 0x55:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 2 * MiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 7,
+ .partitioning = 0};
+ case 0x56:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 16,
+ .partitioning = 0};
+ case 0x57:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 16,
+ .partitioning = 0};
+ case 0x59:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 16,
+ .partitioning = 0};
+ case 0x5A:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 2 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 32,
+ .partitioning = 0};
+ case 0x5B:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0x5C:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 128,
+ .partitioning = 0};
+ case 0x5D:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = 256,
+ .partitioning = 0};
+ case 0x60:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 16 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x61:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 48,
+ .partitioning = 0};
+ case 0x63:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 2 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 4,
+ .partitioning = 0};
+ case 0x66:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 8 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x67:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 16 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x68:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 32 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x70:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 12 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x71:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 16 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x72:
+ return (CacheLevelInfo){.level = 1,
+ .cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
+ .cache_size = 32 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x76:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 2 * MiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 8,
+ .partitioning = 0};
+ case 0x78:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x79:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 128 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x7A:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 256 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x7B:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x7C:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 2};
+ case 0x7D:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x7F:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 2,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x80:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x82:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 256 * KiB,
+ .ways = 8,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x83:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 8,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x84:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 8,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x85:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 8,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x86:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 4,
+ .line_size = 32,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0x87:
+ return (CacheLevelInfo){.level = 2,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xA0:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_DTLB,
+ .cache_size = 4 * KiB,
+ .ways = 0xFF,
+ .line_size = UNDEF,
+ .tlb_entries = 32,
+ .partitioning = 0};
+ case 0xB0:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 128,
+ .partitioning = 0};
+ case 0xB1:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 2 * MiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 8,
+ .partitioning = 0};
+ case 0xB2:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0xB3:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 128,
+ .partitioning = 0};
+ case 0xB4:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 256,
+ .partitioning = 0};
+ case 0xB5:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0xB6:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = 128,
+ .partitioning = 0};
+ case 0xBA:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 64,
+ .partitioning = 0};
+ case 0xC0:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_TLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 8,
+ .partitioning = 0};
+ case 0xC1:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_STLB,
+ .cache_size = 4 * KiB,
+ .ways = 8,
+ .line_size = UNDEF,
+ .tlb_entries = 1024,
+ .partitioning = 0};
+ case 0xC2:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_DTLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 16,
+ .partitioning = 0};
+ case 0xC3:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_STLB,
+ .cache_size = 4 * KiB,
+ .ways = 6,
+ .line_size = UNDEF,
+ .tlb_entries = 1536,
+ .partitioning = 0};
+ case 0xCA:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_STLB,
+ .cache_size = 4 * KiB,
+ .ways = 4,
+ .line_size = UNDEF,
+ .tlb_entries = 512,
+ .partitioning = 0};
+ case 0xD0:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 512 * KiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xD1:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xD2:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 4,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xD6:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xD7:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xD8:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 8,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xDC:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 1 * 1536 * KiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xDD:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 3 * MiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xDE:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 6 * MiB,
+ .ways = 12,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xE2:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 2 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xE3:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 4 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xE4:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 8 * MiB,
+ .ways = 16,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xEA:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 12 * MiB,
+ .ways = 24,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xEB:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 18 * MiB,
+ .ways = 24,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xEC:
+ return (CacheLevelInfo){.level = 3,
+ .cache_type = CPU_FEATURE_CACHE_DATA,
+ .cache_size = 24 * MiB,
+ .ways = 24,
+ .line_size = 64,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xF0:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_PREFETCH,
+ .cache_size = 64 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xF1:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_PREFETCH,
+ .cache_size = 128 * KiB,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ case 0xFF:
+ return (CacheLevelInfo){.level = UNDEF,
+ .cache_type = CPU_FEATURE_CACHE_NULL,
+ .cache_size = UNDEF,
+ .ways = UNDEF,
+ .line_size = UNDEF,
+ .tlb_entries = UNDEF,
+ .partitioning = 0};
+ default:
+ return kEmptyCacheLevelInfo;
+ }
+}
+
+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]);
+ }
+ 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;
+ }
+ 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,
+ .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);
+ }
+}
+
+// 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;
+}
+
+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;
+ }
+ }
+ return X86_UNKNOWN;
+}
+
+static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
+ char* buffer) {
+ const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
+ // We allow calling memcpy from SetString which is only called when requesting
+ // X86BrandString.
+ memcpy(buffer, &leaf, sizeof(Leaf));
+}
+
+void FillX86BrandString(char brand_string[49]) {
+ const Leaf leaf_ext_0 = CpuId(0x80000000);
+ const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
+ SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
+ SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
+ SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
+ brand_string[48] = '\0';
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Introspection functions
+
+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";
+}
--- /dev/null
+// 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_
--- /dev/null
+// 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 "internal/filesystem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined(CPU_FEATURES_MOCK_FILESYSTEM)
+// Implementation will be provided by test/filesystem_for_testing.cc.
+#elif defined(_MSC_VER)
+#include <io.h>
+int CpuFeatures_OpenFile(const char* filename) {
+ int fd = -1;
+ _sopen_s(&fd, filename, _O_RDONLY, _SH_DENYWR, _S_IREAD);
+ return fd;
+}
+
+void CpuFeatures_CloseFile(int file_descriptor) { _close(file_descriptor); }
+
+int CpuFeatures_ReadFile(int file_descriptor, void* buffer,
+ size_t buffer_size) {
+ return _read(file_descriptor, buffer, (unsigned int)buffer_size);
+}
+
+#else
+#include <unistd.h>
+
+int CpuFeatures_OpenFile(const char* filename) {
+ int result;
+ do {
+ result = open(filename, O_RDONLY);
+ } while (result == -1L && errno == EINTR);
+ return result;
+}
+
+void CpuFeatures_CloseFile(int file_descriptor) { close(file_descriptor); }
+
+int CpuFeatures_ReadFile(int file_descriptor, void* buffer,
+ size_t buffer_size) {
+ int result;
+ do {
+ result = read(file_descriptor, buffer, buffer_size);
+ } while (result == -1L && errno == EINTR);
+ return result;
+}
+
+#endif
--- /dev/null
+// 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 "internal/hwcaps.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu_features_macros.h"
+#include "internal/filesystem.h"
+#include "internal/string_view.h"
+
+static bool IsSet(const uint32_t mask, const uint32_t value) {
+ if (mask == 0) return false;
+ return (value & mask) == mask;
+}
+
+bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
+ const HardwareCapabilities hwcaps) {
+ return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) ||
+ IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
+}
+
+#ifdef CPU_FEATURES_TEST
+// In test mode, hwcaps_for_testing will define the following functions.
+HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
+PlatformType CpuFeatures_GetPlatformType(void);
+#else
+
+// Debug facilities
+#if defined(NDEBUG)
+#define D(...)
+#else
+#include <stdio.h>
+#define D(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ fflush(stdout); \
+ } while (0)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of GetElfHwcapFromGetauxval
+////////////////////////////////////////////////////////////////////////////////
+
+#define AT_HWCAP 16
+#define AT_HWCAP2 26
+#define AT_PLATFORM 15
+#define AT_BASE_PLATFORM 24
+
+#if defined(HAVE_STRONG_GETAUXVAL)
+#include <sys/auxv.h>
+static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
+ return getauxval(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.
+//
+// Note that getauxval() can't really be re-implemented here, because its
+// implementation does not parse /proc/self/auxv. Instead it depends on values
+// that are passed by the kernel at process-init time to the C runtime
+// initialization layer.
+
+#include <dlfcn.h>
+
+typedef unsigned long getauxval_func_t(unsigned long);
+
+static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
+ uint32_t ret = 0;
+ void *libc_handle = NULL;
+ getauxval_func_t *func = NULL;
+
+ dlerror(); // Cleaning error state before calling dlopen.
+ libc_handle = dlopen("libc.so", RTLD_NOW);
+ if (!libc_handle) {
+ D("Could not dlopen() C library: %s\n", dlerror());
+ return 0;
+ }
+ func = (getauxval_func_t *)dlsym(libc_handle, "getauxval");
+ if (!func) {
+ D("Could not find getauxval() in C library\n");
+ } else {
+ // Note: getauxval() returns 0 on failure. Doesn't touch errno.
+ ret = (uint32_t)(*func)(hwcap_type);
+ }
+ dlclose(libc_handle);
+ return ret;
+}
+#else
+#error "This platform does not provide hardware capabilities."
+#endif
+
+// Implementation of GetHardwareCapabilities for OS that provide
+// GetElfHwcapFromGetauxval().
+
+// Fallback when getauxval is not available, retrieves hwcaps from
+// "/proc/self/auxv".
+static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
+ struct {
+ uint32_t tag;
+ uint32_t value;
+ } entry;
+ uint32_t result = 0;
+ const char filepath[] = "/proc/self/auxv";
+ const int fd = CpuFeatures_OpenFile(filepath);
+ if (fd < 0) {
+ D("Could not open %s\n", filepath);
+ return 0;
+ }
+ for (;;) {
+ const int ret = CpuFeatures_ReadFile(fd, (char *)&entry, sizeof entry);
+ if (ret < 0) {
+ D("Error while reading %s\n", filepath);
+ break;
+ }
+ // Detect end of list.
+ if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
+ break;
+ }
+ if (entry.tag == hwcap_type) {
+ result = entry.value;
+ break;
+ }
+ }
+ CpuFeatures_CloseFile(fd);
+ return result;
+}
+
+// Retrieves hardware capabilities by first trying to call getauxval, if not
+// available falls back to reading "/proc/self/auxv".
+static unsigned long GetHardwareCapabilitiesFor(uint32_t type) {
+ unsigned long hwcaps = GetElfHwcapFromGetauxval(type);
+ if (!hwcaps) {
+ D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
+ hwcaps = GetElfHwcapFromProcSelfAuxv(type);
+ }
+ return hwcaps;
+}
+
+HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
+ HardwareCapabilities capabilities;
+ capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
+ capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
+ 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);
+
+ 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;
+}
+
+#endif // CPU_FEATURES_TEST
--- /dev/null
+// 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 "internal/stack_line_reader.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "internal/filesystem.h"
+
+void StackLineReader_Initialize(StackLineReader* reader, int fd) {
+ reader->view.ptr = reader->buffer;
+ reader->view.size = 0;
+ reader->skip_mode = false;
+ reader->fd = fd;
+}
+
+// Replaces the content of buffer with bytes from the file.
+static int LoadFullBuffer(StackLineReader* reader) {
+ const int read = CpuFeatures_ReadFile(reader->fd, reader->buffer,
+ STACK_LINE_READER_BUFFER_SIZE);
+ assert(read >= 0);
+ reader->view.ptr = reader->buffer;
+ reader->view.size = read;
+ return read;
+}
+
+// Appends with bytes from the file to buffer, filling the remaining space.
+static int LoadMore(StackLineReader* reader) {
+ char* const ptr = reader->buffer + reader->view.size;
+ const size_t size_to_read = STACK_LINE_READER_BUFFER_SIZE - reader->view.size;
+ const int read = CpuFeatures_ReadFile(reader->fd, ptr, size_to_read);
+ assert(read >= 0);
+ assert(read <= (int)size_to_read);
+ reader->view.size += read;
+ return read;
+}
+
+static int IndexOfEol(StackLineReader* reader) {
+ return CpuFeatures_StringView_IndexOfChar(reader->view, '\n');
+}
+
+// Relocate buffer's pending bytes at the beginning of the array and fills the
+// remaining space with bytes from the file.
+static int BringToFrontAndLoadMore(StackLineReader* reader) {
+ if (reader->view.size && reader->view.ptr != reader->buffer) {
+ memmove(reader->buffer, reader->view.ptr, reader->view.size);
+ }
+ reader->view.ptr = reader->buffer;
+ return LoadMore(reader);
+}
+
+// Loads chunks of buffer size from disks until it contains a newline character
+// or end of file.
+static void SkipToNextLine(StackLineReader* reader) {
+ for (;;) {
+ const int read = LoadFullBuffer(reader);
+ if (read == 0) {
+ break;
+ } else {
+ const int eol_index = IndexOfEol(reader);
+ if (eol_index >= 0) {
+ reader->view =
+ CpuFeatures_StringView_PopFront(reader->view, eol_index + 1);
+ break;
+ }
+ }
+ }
+}
+
+static LineResult CreateLineResult(bool eof, bool full_line, StringView view) {
+ LineResult result;
+ result.eof = eof;
+ result.full_line = full_line;
+ result.line = view;
+ return result;
+}
+
+// Helper methods to provide clearer semantic in StackLineReader_NextLine.
+static LineResult CreateEOFLineResult(StringView view) {
+ return CreateLineResult(true, true, view);
+}
+
+static LineResult CreateTruncatedLineResult(StringView view) {
+ return CreateLineResult(false, false, view);
+}
+
+static LineResult CreateValidLineResult(StringView view) {
+ return CreateLineResult(false, true, view);
+}
+
+LineResult StackLineReader_NextLine(StackLineReader* reader) {
+ if (reader->skip_mode) {
+ SkipToNextLine(reader);
+ reader->skip_mode = false;
+ }
+ {
+ const bool can_load_more =
+ reader->view.size < STACK_LINE_READER_BUFFER_SIZE;
+ int eol_index = IndexOfEol(reader);
+ if (eol_index < 0 && can_load_more) {
+ const int read = BringToFrontAndLoadMore(reader);
+ if (read == 0) {
+ return CreateEOFLineResult(reader->view);
+ }
+ eol_index = IndexOfEol(reader);
+ }
+ if (eol_index < 0) {
+ reader->skip_mode = true;
+ return CreateTruncatedLineResult(reader->view);
+ }
+ {
+ StringView line =
+ CpuFeatures_StringView_KeepFront(reader->view, eol_index);
+ reader->view =
+ CpuFeatures_StringView_PopFront(reader->view, eol_index + 1);
+ return CreateValidLineResult(line);
+ }
+ }
+}
--- /dev/null
+// 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 "internal/string_view.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+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);
+ if (found) {
+ return (int)(found - view.ptr);
+ }
+ }
+ return -1;
+}
+
+int CpuFeatures_StringView_IndexOf(const StringView view,
+ const StringView sub_view) {
+ if (sub_view.size) {
+ StringView remainder = view;
+ while (remainder.size >= sub_view.size) {
+ const int found_index =
+ CpuFeatures_StringView_IndexOfChar(remainder, sub_view.ptr[0]);
+ if (found_index < 0) break;
+ remainder = CpuFeatures_StringView_PopFront(remainder, found_index);
+ if (CpuFeatures_StringView_StartsWith(remainder, sub_view)) {
+ return (int)(remainder.ptr - view.ptr);
+ }
+ remainder = CpuFeatures_StringView_PopFront(remainder, 1);
+ }
+ }
+ return -1;
+}
+
+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 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
+ : false;
+}
+
+StringView CpuFeatures_StringView_PopFront(const StringView str_view,
+ size_t count) {
+ if (count > str_view.size) {
+ return kEmptyStringView;
+ }
+ return view(str_view.ptr + count, str_view.size - count);
+}
+
+StringView CpuFeatures_StringView_PopBack(const StringView str_view,
+ size_t count) {
+ if (count > str_view.size) {
+ return kEmptyStringView;
+ }
+ return view(str_view.ptr, str_view.size - count);
+}
+
+StringView CpuFeatures_StringView_KeepFront(const StringView str_view,
+ size_t count) {
+ return count <= str_view.size ? view(str_view.ptr, count) : str_view;
+}
+
+char CpuFeatures_StringView_Front(const StringView view) {
+ assert(view.size);
+ assert(view.ptr);
+ return view.ptr[0];
+}
+
+char CpuFeatures_StringView_Back(const StringView view) {
+ assert(view.size);
+ return view.ptr[view.size - 1];
+}
+
+StringView CpuFeatures_StringView_TrimWhitespace(StringView view) {
+ while (view.size && isspace(CpuFeatures_StringView_Front(view)))
+ view = CpuFeatures_StringView_PopFront(view, 1);
+ while (view.size && isspace(CpuFeatures_StringView_Back(view)))
+ view = CpuFeatures_StringView_PopBack(view, 1);
+ return view;
+}
+
+static int HexValue(const char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+// Returns -1 if view contains non digits.
+static int ParsePositiveNumberWithBase(const StringView view, int base) {
+ int result = 0;
+ StringView remainder = view;
+ for (; remainder.size;
+ remainder = CpuFeatures_StringView_PopFront(remainder, 1)) {
+ const int value = HexValue(CpuFeatures_StringView_Front(remainder));
+ if (value < 0 || value >= base) return -1;
+ result = (result * base) + value;
+ }
+ return result;
+}
+
+int CpuFeatures_StringView_ParsePositiveNumber(const StringView view) {
+ if (view.size) {
+ const StringView hex_prefix = str("0x");
+ if (CpuFeatures_StringView_StartsWith(view, hex_prefix)) {
+ const StringView span_no_prefix =
+ CpuFeatures_StringView_PopFront(view, hex_prefix.size);
+ return ParsePositiveNumberWithBase(span_no_prefix, 16);
+ }
+ return ParsePositiveNumberWithBase(view, 10);
+ }
+ return -1;
+}
+
+void CpuFeatures_StringView_CopyString(const StringView src, char* dst,
+ size_t dst_size) {
+ if (dst_size > 0) {
+ 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);
+ dst[copy_size] = '\0';
+ }
+}
+
+bool CpuFeatures_StringView_HasWord(const StringView line,
+ const char* const word_str) {
+ const StringView word = str(word_str);
+ StringView remainder = line;
+ for (;;) {
+ const int index_of_word = CpuFeatures_StringView_IndexOf(remainder, word);
+ if (index_of_word < 0) {
+ return false;
+ } else {
+ const StringView before =
+ CpuFeatures_StringView_KeepFront(line, index_of_word);
+ const StringView after =
+ CpuFeatures_StringView_PopFront(line, index_of_word + word.size);
+ const bool valid_before =
+ before.size == 0 || CpuFeatures_StringView_Back(before) == ' ';
+ const bool valid_after =
+ after.size == 0 || CpuFeatures_StringView_Front(after) == ' ';
+ if (valid_before && valid_after) return true;
+ remainder =
+ CpuFeatures_StringView_PopFront(remainder, index_of_word + word.size);
+ }
+ }
+ return false;
+}
+
+bool CpuFeatures_StringView_GetAttributeKeyValue(const StringView line,
+ StringView* key,
+ StringView* value) {
+ const StringView sep = str(": ");
+ const int index_of_separator = CpuFeatures_StringView_IndexOf(line, sep);
+ if (index_of_separator < 0) return false;
+ *value = CpuFeatures_StringView_TrimWhitespace(
+ CpuFeatures_StringView_PopFront(line, index_of_separator + sep.size));
+ *key = CpuFeatures_StringView_TrimWhitespace(
+ CpuFeatures_StringView_KeepFront(line, index_of_separator));
+ return true;
+}
--- /dev/null
+// 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.
+
+// This program dumps current host data to the standard output.
+// Output can be text or json if the `--json` flag is passed.
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu_features_macros.h"
+
+#if defined(CPU_FEATURES_ARCH_X86)
+#include "cpuinfo_x86.h"
+#elif defined(CPU_FEATURES_ARCH_ARM)
+#include "cpuinfo_arm.h"
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+#include "cpuinfo_aarch64.h"
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+#include "cpuinfo_mips.h"
+#elif defined(CPU_FEATURES_ARCH_PPC)
+#include "cpuinfo_ppc.h"
+#endif
+
+// Design principles
+// -----------------
+// We build a tree structure containing all the data to be displayed.
+// Then depending on the output type (text or json) we walk the tree and display
+// the data accordingly.
+
+// We use a bump allocator to allocate strings and nodes of the tree,
+// Memory is not intended to be reclaimed.
+typedef struct {
+ char* ptr;
+ size_t size;
+} BumpAllocator;
+
+char gGlobalBuffer[64 * 1024];
+BumpAllocator gBumpAllocator = {.ptr = gGlobalBuffer,
+ .size = sizeof(gGlobalBuffer)};
+
+static void internal_error() {
+ fputs("internal error\n", stderr);
+ exit(EXIT_FAILURE);
+}
+
+#define ALIGN 8
+
+static void assertAligned() {
+ if ((uintptr_t)(gBumpAllocator.ptr) % ALIGN) internal_error();
+}
+
+static void BA_Align() {
+ while (gBumpAllocator.size && (uintptr_t)(gBumpAllocator.ptr) % ALIGN) {
+ --gBumpAllocator.size;
+ ++gBumpAllocator.ptr;
+ }
+ assertAligned();
+}
+
+// Update the available memory left in the BumpAllocator.
+static void* BA_Bump(size_t size) {
+ assertAligned();
+ // Align size to next 8B boundary.
+ size = (size + ALIGN - 1) / ALIGN * ALIGN;
+ if (gBumpAllocator.size < size) internal_error();
+ void* ptr = gBumpAllocator.ptr;
+ gBumpAllocator.size -= size;
+ gBumpAllocator.ptr += size;
+ return ptr;
+}
+
+// The type of the nodes in the tree.
+typedef enum {
+ NT_INVALID,
+ NT_INT,
+ NT_MAP,
+ NT_MAP_ENTRY,
+ NT_ARRAY,
+ NT_ARRAY_ELEMENT,
+ NT_STRING,
+} NodeType;
+
+// The node in the tree.
+typedef struct Node {
+ NodeType type;
+ unsigned integer;
+ const char* string;
+ struct Node* value;
+ struct Node* next;
+} Node;
+
+// Creates an initialized Node.
+static Node* BA_CreateNode(NodeType type) {
+ Node* tv = (Node*)BA_Bump(sizeof(Node));
+ assert(tv);
+ *tv = (Node){.type = type};
+ return tv;
+}
+
+// Adds an integer node.
+static Node* CreateInt(int value) {
+ Node* tv = BA_CreateNode(NT_INT);
+ tv->integer = value;
+ return tv;
+}
+
+// Adds a string node.
+// `value` must outlive the tree.
+static Node* CreateConstantString(const char* value) {
+ Node* tv = BA_CreateNode(NT_STRING);
+ tv->string = value;
+ return tv;
+}
+
+// Adds a map node.
+static Node* CreateMap() { return BA_CreateNode(NT_MAP); }
+
+// Adds an array node.
+static Node* CreateArray() { return BA_CreateNode(NT_ARRAY); }
+
+// Adds a formatted string node.
+static Node* CreatePrintfString(const char* format, ...) {
+ va_list arglist;
+ va_start(arglist, format);
+ char* const ptr = gBumpAllocator.ptr;
+ const int written = vsnprintf(ptr, gBumpAllocator.size, format, arglist);
+ va_end(arglist);
+ if (written < 0 || written >= (int)gBumpAllocator.size) internal_error();
+ return CreateConstantString((char*)BA_Bump(written));
+}
+
+// Adds a string node.
+static Node* CreateString(const char* value) {
+ return CreatePrintfString("%s", value);
+}
+
+// Adds a map entry node.
+static void AddMapEntry(Node* map, const char* key, Node* value) {
+ assert(map && map->type == NT_MAP);
+ Node* current = map;
+ while (current->next) current = current->next;
+ current->next = (Node*)BA_Bump(sizeof(Node));
+ *current->next = (Node){.type = NT_MAP_ENTRY, .string = key, .value = value};
+}
+
+// Adds an array element node.
+static void AddArrayElement(Node* array, Node* value) {
+ assert(array && array->type == NT_ARRAY);
+ Node* current = array;
+ while (current->next) current = current->next;
+ current->next = (Node*)BA_Bump(sizeof(Node));
+ *current->next = (Node){.type = NT_ARRAY_ELEMENT, .value = value};
+}
+
+static int cmp(const void* p1, const void* p2) {
+ return strcmp(*(const char* const*)p1, *(const char* const*)p2);
+}
+
+#define DEFINE_ADD_FLAGS(HasFeature, FeatureName, FeatureType, LastEnum) \
+ static void AddFlags(Node* map, const FeatureType* features) { \
+ size_t i; \
+ const char* ptrs[LastEnum] = {0}; \
+ size_t count = 0; \
+ for (i = 0; i < LastEnum; ++i) { \
+ if (HasFeature(features, i)) { \
+ ptrs[count] = FeatureName(i); \
+ ++count; \
+ } \
+ } \
+ qsort((void*)ptrs, count, sizeof(char*), cmp); \
+ Node* const array = CreateArray(); \
+ for (i = 0; i < count; ++i) \
+ AddArrayElement(array, CreateConstantString(ptrs[i])); \
+ AddMapEntry(map, "flags", array); \
+ }
+
+#if defined(CPU_FEATURES_ARCH_X86)
+DEFINE_ADD_FLAGS(GetX86FeaturesEnumValue, GetX86FeaturesEnumName, X86Features,
+ X86_LAST_)
+#elif defined(CPU_FEATURES_ARCH_ARM)
+DEFINE_ADD_FLAGS(GetArmFeaturesEnumValue, GetArmFeaturesEnumName, ArmFeatures,
+ ARM_LAST_)
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+DEFINE_ADD_FLAGS(GetAarch64FeaturesEnumValue, GetAarch64FeaturesEnumName,
+ Aarch64Features, AARCH64_LAST_)
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+DEFINE_ADD_FLAGS(GetMipsFeaturesEnumValue, GetMipsFeaturesEnumName,
+ MipsFeatures, MIPS_LAST_)
+#elif defined(CPU_FEATURES_ARCH_PPC)
+DEFINE_ADD_FLAGS(GetPPCFeaturesEnumValue, GetPPCFeaturesEnumName, PPCFeatures,
+ PPC_LAST_)
+#endif
+
+// Prints a json string with characters escaping.
+static void printJsonString(const char* str) {
+ putchar('"');
+ for (; str && *str; ++str) {
+ switch (*str) {
+ case '\"':
+ case '\\':
+ case '/':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ putchar('\\');
+ }
+ putchar(*str);
+ }
+ putchar('"');
+}
+
+// Walks a Node and print it as json.
+static void printJson(const Node* current) {
+ assert(current);
+ switch (current->type) {
+ case NT_INVALID:
+ break;
+ case NT_INT:
+ printf("%d", current->integer);
+ break;
+ case NT_STRING:
+ printJsonString(current->string);
+ break;
+ case NT_ARRAY:
+ putchar('[');
+ if (current->next) printJson(current->next);
+ putchar(']');
+ break;
+ case NT_MAP:
+ putchar('{');
+ if (current->next) printJson(current->next);
+ putchar('}');
+ break;
+ case NT_MAP_ENTRY:
+ printf("\"%s\":", current->string);
+ printJson(current->value);
+ if (current->next) {
+ putchar(',');
+ printJson(current->next);
+ }
+ break;
+ case NT_ARRAY_ELEMENT:
+ printJson(current->value);
+ if (current->next) {
+ putchar(',');
+ printJson(current->next);
+ }
+ break;
+ }
+}
+
+// Walks a Node and print it as text.
+static void printTextField(const Node* current) {
+ switch (current->type) {
+ case NT_INVALID:
+ break;
+ case NT_INT:
+ printf("%3d (0x%02X)", current->integer, current->integer);
+ break;
+ case NT_STRING:
+ fputs(current->string, stdout);
+ break;
+ case NT_ARRAY:
+ if (current->next) printTextField(current->next);
+ break;
+ case NT_MAP:
+ if (current->next) {
+ printf("{");
+ printJson(current->next);
+ printf("}");
+ }
+ break;
+ case NT_MAP_ENTRY:
+ printf("%-15s : ", current->string);
+ printTextField(current->value);
+ if (current->next) {
+ putchar('\n');
+ printTextField(current->next);
+ }
+ break;
+ case NT_ARRAY_ELEMENT:
+ printTextField(current->value);
+ if (current->next) {
+ putchar(',');
+ printTextField(current->next);
+ }
+ break;
+ }
+}
+
+static void printTextRoot(const Node* current) {
+ if (current->type == NT_MAP && current->next) printTextField(current->next);
+}
+
+static void showUsage(const char* name) {
+ printf(
+ "\n"
+ "Usage: %s [options]\n"
+ " Options:\n"
+ " -h | --help Show help message.\n"
+ " -j | --json Format output as json instead of plain text.\n"
+ "\n",
+ name);
+}
+
+static Node* GetCacheTypeString(CacheType cache_type) {
+ switch (cache_type) {
+ case CPU_FEATURE_CACHE_NULL:
+ return CreateConstantString("null");
+ case CPU_FEATURE_CACHE_DATA:
+ return CreateConstantString("data");
+ case CPU_FEATURE_CACHE_INSTRUCTION:
+ return CreateConstantString("instruction");
+ case CPU_FEATURE_CACHE_UNIFIED:
+ return CreateConstantString("unified");
+ case CPU_FEATURE_CACHE_TLB:
+ return CreateConstantString("tlb");
+ case CPU_FEATURE_CACHE_DTLB:
+ return CreateConstantString("dtlb");
+ case CPU_FEATURE_CACHE_STLB:
+ return CreateConstantString("stlb");
+ case CPU_FEATURE_CACHE_PREFETCH:
+ return CreateConstantString("prefetch");
+ }
+}
+
+static void AddCacheInfo(Node* root, const CacheInfo* cache_info) {
+ Node* array = CreateArray();
+ for (int i = 0; i < cache_info->size; ++i) {
+ CacheLevelInfo info = cache_info->levels[i];
+ Node* map = CreateMap();
+ AddMapEntry(map, "level", CreateInt(info.level));
+ AddMapEntry(map, "cache_type", GetCacheTypeString(info.cache_type));
+ AddMapEntry(map, "cache_size", CreateInt(info.cache_size));
+ AddMapEntry(map, "ways", CreateInt(info.ways));
+ AddMapEntry(map, "line_size", CreateInt(info.line_size));
+ AddMapEntry(map, "tlb_entries", CreateInt(info.tlb_entries));
+ AddMapEntry(map, "partitioning", CreateInt(info.partitioning));
+ AddArrayElement(array, map);
+ }
+ AddMapEntry(root, "cache_info", array);
+}
+
+static Node* CreateTree() {
+ Node* root = CreateMap();
+#if defined(CPU_FEATURES_ARCH_X86)
+ char brand_string[49];
+ const X86Info info = GetX86Info();
+ const CacheInfo cache_info = GetX86CacheInfo();
+ FillX86BrandString(brand_string);
+ AddMapEntry(root, "arch", CreateString("x86"));
+ AddMapEntry(root, "brand", CreateString(brand_string));
+ AddMapEntry(root, "family", CreateInt(info.family));
+ AddMapEntry(root, "model", CreateInt(info.model));
+ AddMapEntry(root, "stepping", CreateInt(info.stepping));
+ AddMapEntry(root, "uarch",
+ CreateString(
+ GetX86MicroarchitectureName(GetX86Microarchitecture(&info))));
+ AddFlags(root, &info.features);
+ AddCacheInfo(root, &cache_info);
+#elif defined(CPU_FEATURES_ARCH_ARM)
+ const ArmInfo info = GetArmInfo();
+ AddMapEntry(root, "arch", CreateString("ARM"));
+ AddMapEntry(root, "implementer", CreateInt(info.implementer));
+ AddMapEntry(root, "architecture", CreateInt(info.architecture));
+ AddMapEntry(root, "variant", CreateInt(info.variant));
+ AddMapEntry(root, "part", CreateInt(info.part));
+ AddMapEntry(root, "revision", CreateInt(info.revision));
+ AddFlags(root, &info.features);
+#elif defined(CPU_FEATURES_ARCH_AARCH64)
+ const Aarch64Info info = GetAarch64Info();
+ AddMapEntry(root, "arch", CreateString("aarch64"));
+ AddMapEntry(root, "implementer", CreateInt(info.implementer));
+ AddMapEntry(root, "variant", CreateInt(info.variant));
+ AddMapEntry(root, "part", CreateInt(info.part));
+ AddMapEntry(root, "revision", CreateInt(info.revision));
+ AddFlags(root, &info.features);
+#elif defined(CPU_FEATURES_ARCH_MIPS)
+ const MipsInfo info = GetMipsInfo();
+ AddMapEntry(root, "arch", CreateString("mips"));
+ AddFlags(root, &info.features);
+#elif defined(CPU_FEATURES_ARCH_PPC)
+ const PPCInfo info = GetPPCInfo();
+ const PPCPlatformStrings strings = GetPPCPlatformStrings();
+ AddMapEntry(root, "arch", CreateString("ppc"));
+ AddMapEntry(root, "platform", CreateString(strings.platform));
+ AddMapEntry(root, "model", CreateString(strings.model));
+ AddMapEntry(root, "machine", CreateString(strings.machine));
+ AddMapEntry(root, "cpu", CreateString(strings.cpu));
+ AddMapEntry(root, "instruction", CreateString(strings.type.platform));
+ AddMapEntry(root, "microarchitecture",
+ CreateString(strings.type.base_platform));
+ AddFlags(root, &info.features);
+#endif
+ return root;
+}
+
+int main(int argc, char** argv) {
+ BA_Align();
+ const Node* const root = CreateTree();
+ bool outputJson = false;
+ int i = 1;
+ for (; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (strcmp(arg, "-j") == 0 || strcmp(arg, "--json") == 0) {
+ outputJson = true;
+ } else {
+ showUsage(argv[0]);
+ if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
+ return EXIT_SUCCESS;
+ return EXIT_FAILURE;
+ }
+ }
+ if (outputJson)
+ printJson(root);
+ else
+ printTextRoot(root);
+ putchar('\n');
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#
+# libraries for tests
+#
+
+include_directories(../include)
+add_definitions(-DCPU_FEATURES_TEST)
+
+##------------------------------------------------------------------------------
+add_library(string_view ../src/string_view.c)
+##------------------------------------------------------------------------------
+add_library(filesystem_for_testing filesystem_for_testing.cc)
+target_compile_definitions(filesystem_for_testing PUBLIC CPU_FEATURES_MOCK_FILESYSTEM)
+##------------------------------------------------------------------------------
+add_library(hwcaps_for_testing hwcaps_for_testing.cc)
+target_link_libraries(hwcaps_for_testing filesystem_for_testing)
+##------------------------------------------------------------------------------
+add_library(stack_line_reader ../src/stack_line_reader.c)
+target_compile_definitions(stack_line_reader PUBLIC STACK_LINE_READER_BUFFER_SIZE=1024)
+target_link_libraries(stack_line_reader string_view)
+##------------------------------------------------------------------------------
+add_library(stack_line_reader_for_test ../src/stack_line_reader.c)
+target_compile_definitions(stack_line_reader_for_test PUBLIC STACK_LINE_READER_BUFFER_SIZE=16)
+target_link_libraries(stack_line_reader_for_test string_view filesystem_for_testing)
+##------------------------------------------------------------------------------
+add_library(all_libraries ../src/hwcaps.c ../src/stack_line_reader.c)
+target_link_libraries(all_libraries hwcaps_for_testing stack_line_reader string_view)
+
+#
+# tests
+#
+link_libraries(gtest gmock_main)
+
+## bit_utils_test
+add_executable(bit_utils_test bit_utils_test.cc)
+target_link_libraries(bit_utils_test)
+add_test(NAME bit_utils_test COMMAND bit_utils_test)
+##------------------------------------------------------------------------------
+## string_view_test
+add_executable(string_view_test string_view_test.cc ../src/string_view.c)
+target_link_libraries(string_view_test string_view)
+add_test(NAME string_view_test COMMAND string_view_test)
+##------------------------------------------------------------------------------
+## stack_line_reader_test
+add_executable(stack_line_reader_test stack_line_reader_test.cc)
+target_link_libraries(stack_line_reader_test stack_line_reader_for_test)
+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)
+ target_compile_definitions(cpuinfo_x86_test PUBLIC CPU_FEATURES_MOCK_CPUID_X86)
+ if(APPLE)
+ target_compile_definitions(cpuinfo_x86_test PRIVATE HAVE_SYSCTLBYNAME)
+ endif()
+ target_link_libraries(cpuinfo_x86_test all_libraries)
+ add_test(NAME cpuinfo_x86_test COMMAND cpuinfo_x86_test)
+endif()
+##------------------------------------------------------------------------------
+## cpuinfo_arm_test
+if(PROCESSOR_IS_ARM)
+ add_executable(cpuinfo_arm_test cpuinfo_arm_test.cc ../src/cpuinfo_arm.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)
+ 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)
+ 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)
+ target_link_libraries(cpuinfo_ppc_test all_libraries)
+ add_test(NAME cpuinfo_ppc_test COMMAND cpuinfo_ppc_test)
+endif()
--- /dev/null
+// 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 "internal/bit_utils.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+namespace {
+
+TEST(UtilsTest, IsBitSet) {
+ for (size_t bit_set = 0; bit_set < 32; ++bit_set) {
+ const uint32_t value = 1UL << bit_set;
+ for (uint32_t i = 0; i < 32; ++i) {
+ EXPECT_EQ(IsBitSet(value, i), i == bit_set);
+ }
+ }
+
+ // testing 0, all bits should be 0.
+ for (uint32_t i = 0; i < 32; ++i) {
+ EXPECT_FALSE(IsBitSet(0, i));
+ }
+
+ // testing ~0, all bits should be 1.
+ for (uint32_t i = 0; i < 32; ++i) {
+ EXPECT_TRUE(IsBitSet(-1, i));
+ }
+}
+
+TEST(UtilsTest, ExtractBitRange) {
+ // Extracting all bits gives the same number.
+ EXPECT_EQ(ExtractBitRange(123, 31, 0), 123);
+ // Extracting 1 bit gives parity.
+ EXPECT_EQ(ExtractBitRange(123, 0, 0), 1);
+ EXPECT_EQ(ExtractBitRange(122, 0, 0), 0);
+
+ EXPECT_EQ(ExtractBitRange(0xF0, 7, 4), 0xF);
+ EXPECT_EQ(ExtractBitRange(0x42 << 2, 10, 2), 0x42);
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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 "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+
+namespace cpu_features {
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoAarch64Test, FromHardwareCap) {
+ SetHardwareCapabilities(AARCH64_HWCAP_FP | AARCH64_HWCAP_AES, 0);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetAarch64Info();
+ EXPECT_TRUE(info.features.fp);
+ EXPECT_FALSE(info.features.asimd);
+ EXPECT_FALSE(info.features.evtstrm);
+ EXPECT_TRUE(info.features.aes);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+ EXPECT_FALSE(info.features.crc32);
+ EXPECT_FALSE(info.features.atomics);
+ EXPECT_FALSE(info.features.fphp);
+ EXPECT_FALSE(info.features.asimdhp);
+ EXPECT_FALSE(info.features.cpuid);
+ EXPECT_FALSE(info.features.asimdrdm);
+ EXPECT_FALSE(info.features.jscvt);
+ EXPECT_FALSE(info.features.fcma);
+ EXPECT_FALSE(info.features.lrcpc);
+ EXPECT_FALSE(info.features.dcpop);
+ EXPECT_FALSE(info.features.sha3);
+ EXPECT_FALSE(info.features.sm3);
+ EXPECT_FALSE(info.features.sm4);
+ EXPECT_FALSE(info.features.asimddp);
+ EXPECT_FALSE(info.features.sha512);
+ EXPECT_FALSE(info.features.sve);
+ EXPECT_FALSE(info.features.asimdfhm);
+ EXPECT_FALSE(info.features.dit);
+ EXPECT_FALSE(info.features.uscat);
+ EXPECT_FALSE(info.features.ilrcpc);
+ EXPECT_FALSE(info.features.flagm);
+ EXPECT_FALSE(info.features.ssbs);
+ EXPECT_FALSE(info.features.sb);
+ EXPECT_FALSE(info.features.paca);
+ EXPECT_FALSE(info.features.pacg);
+}
+
+TEST(CpuinfoAarch64Test, FromHardwareCap2) {
+ SetHardwareCapabilities(AARCH64_HWCAP_FP,
+ AARCH64_HWCAP2_SVE2 | AARCH64_HWCAP2_BTI);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetAarch64Info();
+ EXPECT_TRUE(info.features.fp);
+
+ EXPECT_TRUE(info.features.sve2);
+ EXPECT_TRUE(info.features.bti);
+
+ EXPECT_FALSE(info.features.dcpodp);
+ EXPECT_FALSE(info.features.sveaes);
+ EXPECT_FALSE(info.features.svepmull);
+ EXPECT_FALSE(info.features.svebitperm);
+ EXPECT_FALSE(info.features.svesha3);
+ EXPECT_FALSE(info.features.svesm4);
+ EXPECT_FALSE(info.features.flagm2);
+ EXPECT_FALSE(info.features.frint);
+ EXPECT_FALSE(info.features.svei8mm);
+ EXPECT_FALSE(info.features.svef32mm);
+ EXPECT_FALSE(info.features.svef64mm);
+ EXPECT_FALSE(info.features.svebf16);
+ EXPECT_FALSE(info.features.i8mm);
+ EXPECT_FALSE(info.features.bf16);
+ EXPECT_FALSE(info.features.dgh);
+ EXPECT_FALSE(info.features.rng);
+}
+
+TEST(CpuinfoAarch64Test, ARMCortexA53) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(Processor : AArch64 Processor rev 3 (aarch64)
+processor : 0
+processor : 1
+processor : 2
+processor : 3
+processor : 4
+processor : 5
+processor : 6
+processor : 7
+Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer : 0x41
+CPU architecture: AArch64
+CPU variant : 0x0
+CPU part : 0xd03
+CPU revision : 3)");
+ const auto info = GetAarch64Info();
+ EXPECT_EQ(info.implementer, 0x41);
+ EXPECT_EQ(info.variant, 0x0);
+ EXPECT_EQ(info.part, 0xd03);
+ EXPECT_EQ(info.revision, 3);
+
+ EXPECT_TRUE(info.features.fp);
+ EXPECT_TRUE(info.features.asimd);
+ EXPECT_TRUE(info.features.evtstrm);
+ EXPECT_TRUE(info.features.aes);
+ EXPECT_TRUE(info.features.pmull);
+ EXPECT_TRUE(info.features.sha1);
+ EXPECT_TRUE(info.features.sha2);
+ EXPECT_TRUE(info.features.crc32);
+
+ EXPECT_FALSE(info.features.atomics);
+ EXPECT_FALSE(info.features.fphp);
+ EXPECT_FALSE(info.features.asimdhp);
+ EXPECT_FALSE(info.features.cpuid);
+ EXPECT_FALSE(info.features.asimdrdm);
+ EXPECT_FALSE(info.features.jscvt);
+ EXPECT_FALSE(info.features.fcma);
+ EXPECT_FALSE(info.features.lrcpc);
+ EXPECT_FALSE(info.features.dcpop);
+ EXPECT_FALSE(info.features.sha3);
+ EXPECT_FALSE(info.features.sm3);
+ EXPECT_FALSE(info.features.sm4);
+ EXPECT_FALSE(info.features.asimddp);
+ EXPECT_FALSE(info.features.sha512);
+ EXPECT_FALSE(info.features.sve);
+ EXPECT_FALSE(info.features.asimdfhm);
+ EXPECT_FALSE(info.features.dit);
+ EXPECT_FALSE(info.features.uscat);
+ EXPECT_FALSE(info.features.ilrcpc);
+ EXPECT_FALSE(info.features.flagm);
+ EXPECT_FALSE(info.features.ssbs);
+ EXPECT_FALSE(info.features.sb);
+ EXPECT_FALSE(info.features.paca);
+ EXPECT_FALSE(info.features.pacg);
+ EXPECT_FALSE(info.features.dcpodp);
+ EXPECT_FALSE(info.features.sve2);
+ EXPECT_FALSE(info.features.sveaes);
+ EXPECT_FALSE(info.features.svepmull);
+ EXPECT_FALSE(info.features.svebitperm);
+ EXPECT_FALSE(info.features.svesha3);
+ EXPECT_FALSE(info.features.svesm4);
+ EXPECT_FALSE(info.features.flagm2);
+ EXPECT_FALSE(info.features.frint);
+ EXPECT_FALSE(info.features.svei8mm);
+ EXPECT_FALSE(info.features.svef32mm);
+ EXPECT_FALSE(info.features.svef64mm);
+ EXPECT_FALSE(info.features.svebf16);
+ EXPECT_FALSE(info.features.i8mm);
+ EXPECT_FALSE(info.features.bf16);
+ EXPECT_FALSE(info.features.dgh);
+ EXPECT_FALSE(info.features.rng);
+ EXPECT_FALSE(info.features.bti);
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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_arm.h"
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+
+namespace cpu_features {
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoArmTest, FromHardwareCap) {
+ SetHardwareCapabilities(ARM_HWCAP_NEON, ARM_HWCAP2_AES | ARM_HWCAP2_CRC32);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetArmInfo();
+ EXPECT_TRUE(info.features.vfp); // triggered by vfpv3
+ EXPECT_TRUE(info.features.vfpv3); // triggered by neon
+ EXPECT_TRUE(info.features.neon);
+ EXPECT_TRUE(info.features.aes);
+ EXPECT_TRUE(info.features.crc32);
+
+ EXPECT_FALSE(info.features.vfpv4);
+ EXPECT_FALSE(info.features.iwmmxt);
+ EXPECT_FALSE(info.features.crunch);
+ EXPECT_FALSE(info.features.thumbee);
+ EXPECT_FALSE(info.features.vfpv3d16);
+ EXPECT_FALSE(info.features.idiva);
+ EXPECT_FALSE(info.features.idivt);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+
+ // check some random features with EnumValue():
+ EXPECT_TRUE(GetArmFeaturesEnumValue(&info.features, ARM_VFP));
+ EXPECT_FALSE(GetArmFeaturesEnumValue(&info.features, ARM_VFPV4));
+ // out of bound EnumValue() check
+ EXPECT_FALSE(GetArmFeaturesEnumValue(&info.features, (ArmFeaturesEnum)~0x0));
+}
+
+TEST(CpuinfoArmTest, ODroidFromCpuInfo) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor : 0
+model name : ARMv7 Processor rev 3 (v71)
+BogoMIPS : 120.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x2
+CPU part : 0xc0f
+CPU revision : 3)");
+ const auto info = GetArmInfo();
+ EXPECT_EQ(info.implementer, 0x41);
+ EXPECT_EQ(info.variant, 0x2);
+ EXPECT_EQ(info.part, 0xc0f);
+ EXPECT_EQ(info.revision, 3);
+ EXPECT_EQ(info.architecture, 7);
+
+ EXPECT_FALSE(info.features.swp);
+ EXPECT_TRUE(info.features.half);
+ EXPECT_TRUE(info.features.thumb);
+ EXPECT_FALSE(info.features._26bit);
+ EXPECT_TRUE(info.features.fastmult);
+ EXPECT_FALSE(info.features.fpa);
+ EXPECT_TRUE(info.features.vfp);
+ EXPECT_TRUE(info.features.edsp);
+ EXPECT_FALSE(info.features.java);
+ EXPECT_FALSE(info.features.iwmmxt);
+ EXPECT_FALSE(info.features.crunch);
+ EXPECT_FALSE(info.features.thumbee);
+ EXPECT_TRUE(info.features.neon);
+ EXPECT_TRUE(info.features.vfpv3);
+ EXPECT_FALSE(info.features.vfpv3d16);
+ EXPECT_TRUE(info.features.tls);
+ EXPECT_TRUE(info.features.vfpv4);
+ EXPECT_TRUE(info.features.idiva);
+ EXPECT_TRUE(info.features.idivt);
+ EXPECT_TRUE(info.features.vfpd32);
+ EXPECT_TRUE(info.features.lpae);
+ EXPECT_FALSE(info.features.evtstrm);
+ EXPECT_FALSE(info.features.aes);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+ EXPECT_FALSE(info.features.crc32);
+}
+
+// Linux test-case
+TEST(CpuinfoArmTest, RaspberryPiZeroFromCpuInfo) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor : 0
+model name : ARMv6-compatible processor rev 7 (v6l)
+BogoMIPS : 697.95
+Features : half thumb fastmult vfp edsp java tls
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xb76
+CPU revision : 7
+
+Hardware : BCM2835
+Revision : 9000c1
+Serial : 000000006cd946f3)");
+ const auto info = GetArmInfo();
+ EXPECT_EQ(info.implementer, 0x41);
+ EXPECT_EQ(info.variant, 0x0);
+ EXPECT_EQ(info.part, 0xb76);
+ EXPECT_EQ(info.revision, 7);
+ EXPECT_EQ(info.architecture, 6);
+
+ EXPECT_FALSE(info.features.swp);
+ EXPECT_TRUE(info.features.half);
+ EXPECT_TRUE(info.features.thumb);
+ EXPECT_FALSE(info.features._26bit);
+ EXPECT_TRUE(info.features.fastmult);
+ EXPECT_FALSE(info.features.fpa);
+ EXPECT_TRUE(info.features.vfp);
+ EXPECT_TRUE(info.features.edsp);
+ EXPECT_TRUE(info.features.java);
+ EXPECT_FALSE(info.features.iwmmxt);
+ EXPECT_FALSE(info.features.crunch);
+ EXPECT_FALSE(info.features.thumbee);
+ EXPECT_FALSE(info.features.neon);
+ EXPECT_FALSE(info.features.vfpv3);
+ EXPECT_FALSE(info.features.vfpv3d16);
+ EXPECT_TRUE(info.features.tls);
+ EXPECT_FALSE(info.features.vfpv4);
+ EXPECT_FALSE(info.features.idiva);
+ EXPECT_FALSE(info.features.idivt);
+ EXPECT_FALSE(info.features.vfpd32);
+ EXPECT_FALSE(info.features.lpae);
+ EXPECT_FALSE(info.features.evtstrm);
+ EXPECT_FALSE(info.features.aes);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+ EXPECT_FALSE(info.features.crc32);
+}
+
+TEST(CpuinfoArmTest, MarvellArmadaFromCpuInfo) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor : 0
+model name : ARMv7 Processor rev 1 (v7l)
+BogoMIPS : 50.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpd32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x4
+CPU part : 0xc09
+CPU revision : 1
+
+processor : 1
+model name : ARMv7 Processor rev 1 (v7l)
+BogoMIPS : 50.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpd32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x4
+CPU part : 0xc09
+CPU revision : 1
+
+Hardware : Marvell Armada 380/385 (Device Tree)
+Revision : 0000
+Serial : 0000000000000000)");
+ const auto info = GetArmInfo();
+ EXPECT_EQ(info.implementer, 0x41);
+ EXPECT_EQ(info.variant, 0x4);
+ EXPECT_EQ(info.part, 0xc09);
+ EXPECT_EQ(info.revision, 1);
+ EXPECT_EQ(info.architecture, 7);
+
+ EXPECT_FALSE(info.features.swp);
+ EXPECT_TRUE(info.features.half);
+ EXPECT_TRUE(info.features.thumb);
+ EXPECT_FALSE(info.features._26bit);
+ EXPECT_TRUE(info.features.fastmult);
+ EXPECT_FALSE(info.features.fpa);
+ EXPECT_TRUE(info.features.vfp);
+ EXPECT_TRUE(info.features.edsp);
+ EXPECT_FALSE(info.features.java);
+ EXPECT_FALSE(info.features.iwmmxt);
+ EXPECT_FALSE(info.features.crunch);
+ EXPECT_FALSE(info.features.thumbee);
+ EXPECT_TRUE(info.features.neon);
+ EXPECT_TRUE(info.features.vfpv3);
+ EXPECT_FALSE(info.features.vfpv3d16);
+ EXPECT_TRUE(info.features.tls);
+ EXPECT_FALSE(info.features.vfpv4);
+ EXPECT_FALSE(info.features.idiva);
+ EXPECT_FALSE(info.features.idivt);
+ EXPECT_TRUE(info.features.vfpd32);
+ EXPECT_FALSE(info.features.lpae);
+ EXPECT_FALSE(info.features.evtstrm);
+ EXPECT_FALSE(info.features.aes);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+ EXPECT_FALSE(info.features.crc32);
+}
+
+// Android test-case
+// http://code.google.com/p/android/issues/detail?id=10812
+TEST(CpuinfoArmTest, InvalidArmv7) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(Processor : ARMv6-compatible processor rev 6 (v6l)
+BogoMIPS : 199.47
+Features : swp half thumb fastmult vfp edsp java
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xb76
+CPU revision : 6
+
+Hardware : SPICA
+Revision : 0020
+Serial : 33323613546d00ec )");
+ const auto info = GetArmInfo();
+ EXPECT_EQ(info.architecture, 6);
+
+ EXPECT_TRUE(info.features.swp);
+ EXPECT_TRUE(info.features.half);
+ EXPECT_TRUE(info.features.thumb);
+ EXPECT_FALSE(info.features._26bit);
+ EXPECT_TRUE(info.features.fastmult);
+ EXPECT_FALSE(info.features.fpa);
+ EXPECT_TRUE(info.features.vfp);
+ EXPECT_TRUE(info.features.edsp);
+ EXPECT_TRUE(info.features.java);
+ EXPECT_FALSE(info.features.iwmmxt);
+ EXPECT_FALSE(info.features.crunch);
+ EXPECT_FALSE(info.features.thumbee);
+ EXPECT_FALSE(info.features.neon);
+ EXPECT_FALSE(info.features.vfpv3);
+ EXPECT_FALSE(info.features.vfpv3d16);
+ EXPECT_FALSE(info.features.tls);
+ EXPECT_FALSE(info.features.vfpv4);
+ EXPECT_FALSE(info.features.idiva);
+ EXPECT_FALSE(info.features.idivt);
+ EXPECT_FALSE(info.features.vfpd32);
+ EXPECT_FALSE(info.features.lpae);
+ EXPECT_FALSE(info.features.evtstrm);
+ EXPECT_FALSE(info.features.aes);
+ EXPECT_FALSE(info.features.pmull);
+ EXPECT_FALSE(info.features.sha1);
+ EXPECT_FALSE(info.features.sha2);
+ EXPECT_FALSE(info.features.crc32);
+}
+
+// Android test-case
+// https://crbug.com/341598.
+TEST(CpuinfoArmTest, InvalidNeon) {
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(Processor: ARMv7 Processory rev 0 (v71)
+processor: 0
+BogoMIPS: 13.50
+
+Processor: 1
+BogoMIPS: 13.50
+
+Features: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant: 0x1
+CPU part: 0x04d
+CPU revision: 0
+
+Hardware: SAMSUNG M2
+Revision: 0010
+Serial: 00001e030000354e)");
+ const auto info = GetArmInfo();
+ EXPECT_TRUE(info.features.swp);
+ EXPECT_FALSE(info.features.neon);
+}
+
+// The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
+// support.
+TEST(CpuinfoArmTest, Nexus4_0x510006f2) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(CPU implementer : 0x51
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0x6f
+CPU revision : 2)");
+ const auto info = GetArmInfo();
+ EXPECT_TRUE(info.features.idiva);
+ EXPECT_TRUE(info.features.idivt);
+
+ EXPECT_EQ(GetArmCpuId(&info), 0x510006f2);
+}
+
+// The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report IDIV
+// support.
+TEST(CpuinfoArmTest, Nexus4_0x510006f3) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(CPU implementer : 0x51
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0x6f
+CPU revision : 3)");
+ const auto info = GetArmInfo();
+ EXPECT_TRUE(info.features.idiva);
+ EXPECT_TRUE(info.features.idivt);
+
+ EXPECT_EQ(GetArmCpuId(&info), 0x510006f3);
+}
+
+// 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();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(Processor : ARMv7 Processor rev 0 (v7l)
+BogoMIPS : 629.14
+Features : swp half thumb fastmult vfp edsp neon vfpv3
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xc08
+CPU revision : 0
+
+Hardware : Goldfish
+Revision : 0000
+Serial : 0000000000000000)");
+ const auto info = GetArmInfo();
+ EXPECT_TRUE(info.features.idiva);
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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_mips.h"
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+#include "internal/stack_line_reader.h"
+#include "internal/string_view.h"
+
+namespace cpu_features {
+
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpuinfoMipsTest, FromHardwareCapBoth) {
+ SetHardwareCapabilities(MIPS_HWCAP_MSA | MIPS_HWCAP_R6, 0);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetMipsInfo();
+ EXPECT_TRUE(info.features.msa);
+ EXPECT_FALSE(info.features.eva);
+ EXPECT_TRUE(info.features.r6);
+}
+
+TEST(CpuinfoMipsTest, FromHardwareCapOnlyOne) {
+ SetHardwareCapabilities(MIPS_HWCAP_MSA, 0);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetMipsInfo();
+ EXPECT_TRUE(info.features.msa);
+ EXPECT_FALSE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, Ci40) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(system type : IMG Pistachio SoC (B0)
+machine : IMG Marduk – Ci40 with cc2520
+processor : 0
+cpu model : MIPS interAptiv (multi) V2.0 FPU V0.0
+BogoMIPS : 363.72
+wait instruction : yes
+microsecond timers : yes
+tlb_entries : 64
+extra interrupt vector : yes
+hardware watchpoint : yes, count: 4, address/irw mask: [0x0ffc, 0x0ffc, 0x0ffb, 0x0ffb]
+isa : mips1 mips2 mips32r1 mips32r2
+ASEs implemented : mips16 dsp mt eva
+shadow register sets : 1
+kscratch registers : 0
+package : 0
+core : 0
+VCED exceptions : not available
+VCEI exceptions : not available
+VPE : 0
+)");
+ const auto info = GetMipsInfo();
+ EXPECT_FALSE(info.features.msa);
+ EXPECT_TRUE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, AR7161) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(system type : Atheros AR7161 rev 2
+machine : NETGEAR WNDR3700/WNDR3800/WNDRMAC
+processor : 0
+cpu model : MIPS 24Kc V7.4
+BogoMIPS : 452.19
+wait instruction : yes
+microsecond timers : yes
+tlb_entries : 16
+extra interrupt vector : yes
+hardware watchpoint : yes, count: 4, address/irw mask: [0x0000, 0x0f98, 0x0f78, 0x0df8]
+ASEs implemented : mips16
+shadow register sets : 1
+kscratch registers : 0
+core : 0
+VCED exceptions : not available
+VCEI exceptions : not available
+)");
+ const auto info = GetMipsInfo();
+ EXPECT_FALSE(info.features.msa);
+ EXPECT_FALSE(info.features.eva);
+}
+
+TEST(CpuinfoMipsTest, Goldfish) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(system type : MIPS-Goldfish
+Hardware : goldfish
+Revison : 1
+processor : 0
+cpu model : MIPS 24Kc V0.0 FPU V0.0
+BogoMIPS : 1042.02
+wait instruction : yes
+microsecond timers : yes
+tlb_entries : 16
+extra interrupt vector : yes
+hardware watchpoint : yes, count: 1, address/irw mask: [0x0ff8]
+ASEs implemented :
+shadow register sets : 1
+core : 0
+VCED exceptions : not available
+VCEI exceptions : not available
+)");
+ const auto info = GetMipsInfo();
+ EXPECT_FALSE(info.features.msa);
+ EXPECT_FALSE(info.features.eva);
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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 "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "hwcaps_for_testing.h"
+#include "internal/string_view.h"
+
+namespace cpu_features {
+namespace {
+
+void DisableHardwareCapabilities() { SetHardwareCapabilities(0, 0); }
+
+TEST(CpustringsPPCTest, FromHardwareCap) {
+ SetHardwareCapabilities(PPC_FEATURE_HAS_FPU | PPC_FEATURE_HAS_VSX,
+ PPC_FEATURE2_ARCH_3_00);
+ GetEmptyFilesystem(); // disabling /proc/cpuinfo
+ const auto info = GetPPCInfo();
+ EXPECT_TRUE(info.features.fpu);
+ EXPECT_FALSE(info.features.mmu);
+ EXPECT_TRUE(info.features.vsx);
+ EXPECT_TRUE(info.features.arch300);
+ EXPECT_FALSE(info.features.power4);
+ EXPECT_FALSE(info.features.altivec);
+ EXPECT_FALSE(info.features.vcrypto);
+ EXPECT_FALSE(info.features.htm);
+}
+
+TEST(CpustringsPPCTest, Blade) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(processor : 14
+cpu : POWER7 (architected), altivec supported
+clock : 3000.000000MHz
+revision : 2.1 (pvr 003f 0201)
+
+processor : 15
+cpu : POWER7 (architected), altivec supported
+clock : 3000.000000MHz
+revision : 2.1 (pvr 003f 0201)
+
+timebase : 512000000
+platform : pSeries
+model : IBM,8406-70Y
+machine : CHRP IBM,8406-70Y)");
+ SetPlatformTypes("power7", "power8");
+ const auto strings = GetPPCPlatformStrings();
+ ASSERT_STREQ(strings.platform, "pSeries");
+ ASSERT_STREQ(strings.model, "IBM,8406-70Y");
+ ASSERT_STREQ(strings.machine, "CHRP IBM,8406-70Y");
+ ASSERT_STREQ(strings.cpu, "POWER7 (architected), altivec supported");
+ ASSERT_STREQ(strings.type.platform, "power7");
+ ASSERT_STREQ(strings.type.base_platform, "power8");
+}
+
+TEST(CpustringsPPCTest, Firestone) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(processor : 126
+cpu : POWER8 (raw), altivec supported
+clock : 2061.000000MHz
+revision : 2.0 (pvr 004d 0200)
+
+processor : 127
+cpu : POWER8 (raw), altivec supported
+clock : 2061.000000MHz
+revision : 2.0 (pvr 004d 0200)
+
+timebase : 512000000
+platform : PowerNV
+model : 8335-GTA
+machine : PowerNV 8335-GTA
+firmware : OPAL v3)");
+ const auto strings = GetPPCPlatformStrings();
+ ASSERT_STREQ(strings.platform, "PowerNV");
+ ASSERT_STREQ(strings.model, "8335-GTA");
+ ASSERT_STREQ(strings.machine, "PowerNV 8335-GTA");
+ ASSERT_STREQ(strings.cpu, "POWER8 (raw), altivec supported");
+}
+
+TEST(CpustringsPPCTest, w8) {
+ DisableHardwareCapabilities();
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo",
+ R"(processor : 143
+cpu : POWER9, altivec supported
+clock : 2300.000000MHz
+revision : 2.2 (pvr 004e 1202)
+
+timebase : 512000000
+platform : PowerNV
+model : 0000000000000000
+machine : PowerNV 0000000000000000
+firmware : OPAL
+MMU : Radix)");
+ const auto strings = GetPPCPlatformStrings();
+ ASSERT_STREQ(strings.platform, "PowerNV");
+ ASSERT_STREQ(strings.model, "0000000000000000");
+ ASSERT_STREQ(strings.machine, "PowerNV 0000000000000000");
+ ASSERT_STREQ(strings.cpu, "POWER9, altivec supported");
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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_x86.h"
+
+#include <cassert>
+#include <cstdio>
+#include <map>
+#include <set>
+#if defined(CPU_FEATURES_OS_WINDOWS)
+#include <windows.h> // IsProcessorFeaturePresent
+#endif // CPU_FEATURES_OS_WINDOWS
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+#include "internal/cpuid_x86.h"
+
+namespace cpu_features {
+
+class FakeCpu {
+ public:
+ Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) const {
+ const auto itr = cpuid_leaves_.find(std::make_pair(leaf_id, ecx));
+ if (itr != cpuid_leaves_.end()) {
+ return itr->second;
+ }
+ return {0, 0, 0, 0};
+ }
+
+ uint32_t GetXCR0Eax() const { return xcr0_eax_; }
+
+ void SetLeaves(std::map<std::pair<uint32_t, int>, Leaf> configuration) {
+ cpuid_leaves_ = std::move(configuration);
+ }
+
+ void SetOsBackupsExtendedRegisters(bool os_backups_extended_registers) {
+ xcr0_eax_ = os_backups_extended_registers ? -1 : 0;
+ }
+
+#if defined(CPU_FEATURES_OS_DARWIN)
+ bool GetDarwinSysCtlByName(std::string name) const {
+ return darwin_sysctlbyname_.count(name);
+ }
+
+ void SetDarwinSysCtlByName(std::string name) {
+ darwin_sysctlbyname_.insert(name);
+ }
+#endif // CPU_FEATURES_OS_DARWIN
+
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ return windows_isprocessorfeaturepresent_.count(ProcessorFeature);
+ }
+
+ void SetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ windows_isprocessorfeaturepresent_.insert(ProcessorFeature);
+ }
+#endif // CPU_FEATURES_OS_WINDOWS
+
+ private:
+ std::map<std::pair<uint32_t, int>, Leaf> cpuid_leaves_;
+#if defined(CPU_FEATURES_OS_DARWIN)
+ std::set<std::string> darwin_sysctlbyname_;
+#endif // CPU_FEATURES_OS_DARWIN
+#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;
+
+extern "C" Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
+ return g_fake_cpu->GetCpuidLeaf(leaf_id, ecx);
+}
+
+extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
+
+#if defined(CPU_FEATURES_OS_DARWIN)
+extern "C" bool GetDarwinSysCtlByName(const char* name) {
+ return g_fake_cpu->GetDarwinSysCtlByName(name);
+}
+#endif // CPU_FEATURES_OS_DARWIN
+
+#if defined(CPU_FEATURES_OS_WINDOWS)
+extern "C" bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ return g_fake_cpu->GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
+}
+#endif // CPU_FEATURES_OS_WINDOWS
+
+namespace {
+
+class CpuidX86Test : public ::testing::Test {
+ protected:
+ void SetUp() override { g_fake_cpu = new FakeCpu(); }
+ void TearDown() override { delete g_fake_cpu; }
+};
+
+TEST_F(CpuidX86Test, SandyBridge) {
+ g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x02A);
+ EXPECT_EQ(info.stepping, 0x06);
+ // Leaf 7 is zeroed out so none of the Leaf 7 flags are set.
+ const auto features = info.features;
+ EXPECT_FALSE(features.erms);
+ EXPECT_FALSE(features.avx2);
+ EXPECT_FALSE(features.avx512f);
+ EXPECT_FALSE(features.avx512cd);
+ EXPECT_FALSE(features.avx512er);
+ EXPECT_FALSE(features.avx512pf);
+ EXPECT_FALSE(features.avx512bw);
+ EXPECT_FALSE(features.avx512dq);
+ EXPECT_FALSE(features.avx512vl);
+ EXPECT_FALSE(features.avx512ifma);
+ EXPECT_FALSE(features.avx512vbmi);
+ EXPECT_FALSE(features.avx512vbmi2);
+ EXPECT_FALSE(features.avx512vnni);
+ EXPECT_FALSE(features.avx512bitalg);
+ EXPECT_FALSE(features.avx512vpopcntdq);
+ EXPECT_FALSE(features.avx512_4vnniw);
+ EXPECT_FALSE(features.avx512_4fmaps);
+ // All old cpu features should be set.
+ EXPECT_TRUE(features.aes);
+ EXPECT_TRUE(features.ssse3);
+ EXPECT_TRUE(features.sse4_1);
+ EXPECT_TRUE(features.sse4_2);
+ EXPECT_TRUE(features.avx);
+ EXPECT_FALSE(features.sha);
+ EXPECT_TRUE(features.popcnt);
+ EXPECT_FALSE(features.movbe);
+ EXPECT_FALSE(features.rdrnd);
+}
+
+const int KiB = 1024;
+const int MiB = 1024 * KiB;
+
+TEST_F(CpuidX86Test, SandyBridgeTestOsSupport) {
+ g_fake_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);
+ EXPECT_FALSE(GetX86Info().features.avx);
+ // avx is disabled if os does not support backing up ymm registers.
+ g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+ EXPECT_TRUE(GetX86Info().features.avx);
+}
+
+TEST_F(CpuidX86Test, SkyLake) {
+ g_fake_cpu->SetOsBackupsExtendedRegisters(true);
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x04E);
+ EXPECT_EQ(info.stepping, 0x03);
+ EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_SKL);
+}
+
+TEST_F(CpuidX86Test, Branding) {
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000121, 0x2C100000}},
+ {{0x80000002, 0}, Leaf{0x65746E49, 0x2952286C, 0x726F4320, 0x4D542865}},
+ {{0x80000003, 0}, Leaf{0x37692029, 0x3035362D, 0x43205530, 0x40205550}},
+ {{0x80000004, 0}, Leaf{0x352E3220, 0x7A484730, 0x00000000, 0x00000000}},
+ });
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz");
+}
+
+TEST_F(CpuidX86Test, KabyLakeCache) {
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+ {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 1}, Leaf{0x1C004122, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 2}, Leaf{0x1C004143, 0x00C0003F, 0x000003FF, 0x00000000}},
+ {{0x00000004, 3}, Leaf{0x1C03C163, 0x02C0003F, 0x00001FFF, 0x00000002}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000121, 0x2C100000}},
+ {{0x80000002, 0}, Leaf{0x65746E49, 0x2952286C, 0x726F4320, 0x4D542865}},
+ {{0x80000003, 0}, Leaf{0x37692029, 0x3035362D, 0x43205530, 0x40205550}},
+ });
+ 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, 32 * KiB);
+ EXPECT_EQ(info.levels[1].ways, 8);
+ EXPECT_EQ(info.levels[1].line_size, 64);
+ EXPECT_EQ(info.levels[1].tlb_entries, 64);
+ 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, 256 * KiB);
+ EXPECT_EQ(info.levels[2].ways, 4);
+ 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, 6 * MiB);
+ EXPECT_EQ(info.levels[3].ways, 12);
+ EXPECT_EQ(info.levels[3].line_size, 64);
+ EXPECT_EQ(info.levels[3].tlb_entries, 8192);
+ EXPECT_EQ(info.levels[3].partitioning, 1);
+}
+
+TEST_F(CpuidX86Test, HSWCache) {
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
+ {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 1}, Leaf{0x1C004122, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 2}, Leaf{0x1C004143, 0x01C0003F, 0x000001FF, 0x00000000}},
+ {{0x00000004, 3}, Leaf{0x1C03C163, 0x02C0003F, 0x00001FFF, 0x00000006}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x029C67AF, 0x00000000, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000121, 0x2C100000}},
+ {{0x80000002, 0}, Leaf{0x65746E49, 0x2952286C, 0x726F4320, 0x4D542865}},
+ {{0x80000003, 0}, Leaf{0x37692029, 0x3035362D, 0x43205530, 0x40205550}},
+ });
+ 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, 32 * KiB);
+ EXPECT_EQ(info.levels[1].ways, 8);
+ EXPECT_EQ(info.levels[1].line_size, 64);
+ EXPECT_EQ(info.levels[1].tlb_entries, 64);
+ 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, 256 * KiB);
+ EXPECT_EQ(info.levels[2].ways, 8);
+ EXPECT_EQ(info.levels[2].line_size, 64);
+ EXPECT_EQ(info.levels[2].tlb_entries, 512);
+ 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, 12);
+ 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/AuthenticAMD0630F81_K15_Godavari_CPUID.txt
+TEST_F(CpuidX86Test, AMD_K15) {
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
+ {{0x00000001, 0}, Leaf{0x00630F81, 0x00040800, 0x3E98320B, 0x178BFBFF}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x8000001E, 0x68747541, 0x444D4163, 0x69746E65}},
+ {{0x80000001, 0}, Leaf{0x00630F81, 0x10000000, 0x0FEBBFFF, 0x2FD3FBFF}},
+ {{0x80000002, 0}, Leaf{0x20444D41, 0x372D3841, 0x4B303736, 0x64615220}},
+ {{0x80000003, 0}, Leaf{0x206E6F65, 0x202C3752, 0x43203031, 0x75706D6F}},
+ {{0x80000004, 0}, Leaf{0x43206574, 0x7365726F, 0x2B433420, 0x00204736}},
+ {{0x80000005, 0}, Leaf{0xFF40FF18, 0xFF40FF30, 0x10040140, 0x60030140}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "AuthenticAMD");
+ EXPECT_EQ(info.family, 0x15);
+ EXPECT_EQ(info.model, 0x38);
+ EXPECT_EQ(info.stepping, 0x01);
+ EXPECT_EQ(GetX86Microarchitecture(&info),
+ X86Microarchitecture::AMD_BULLDOZER);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "AMD A8-7670K Radeon R7, 10 Compute Cores 4C+6G ");
+}
+
+// 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);
+#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)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor :
+flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000106A2, 0x00100800, 0x00BCE3BD, 0xBFEBFBFF}},
+ {{0x00000002, 0}, Leaf{0x55035A01, 0x00F0B0E3, 0x00000000, 0x09CA212C}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004122, 0x00C0003F, 0x0000007F, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004143, 0x01C0003F, 0x000001FF, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C03C163, 0x03C0003F, 0x00000FFF, 0x00000002}},
+ {{0x00000005, 0}, Leaf{0x00000040, 0x00000040, 0x00000003, 0x00021120}},
+ {{0x00000006, 0}, Leaf{0x00000001, 0x00000002, 0x00000001, 0x00000000}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000008, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000009, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x0000000A, 0}, Leaf{0x07300403, 0x00000000, 0x00000000, 0x00000603}},
+ {{0x0000000B, 0}, Leaf{0x00000001, 0x00000001, 0x00000100, 0x00000000}},
+ {{0x0000000B, 0}, Leaf{0x00000004, 0x00000002, 0x00000201, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000001, 0x28100000}},
+ {{0x80000002, 0}, Leaf{0x756E6547, 0x20656E69, 0x65746E49, 0x2952286C}},
+ {{0x80000003, 0}, Leaf{0x55504320, 0x20202020, 0x20202020, 0x40202020}},
+ {{0x80000004, 0}, Leaf{0x30303020, 0x20402030, 0x37382E31, 0x007A4847}},
+ {{0x80000005, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000006, 0}, Leaf{0x00000000, 0x00000000, 0x01006040, 0x00000000}},
+ {{0x80000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000100}},
+ {{0x80000008, 0}, Leaf{0x00003028, 0x00000000, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x1A);
+ EXPECT_EQ(info.stepping, 0x02);
+ EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_NHM);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "Genuine Intel(R) CPU @ 0000 @ 1.87GHz");
+
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_TRUE(info.features.sse2);
+ EXPECT_TRUE(info.features.sse3);
+#ifndef 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
+}
+
+// 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);
+#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)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(
+flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x00030673, 0x00100800, 0x41D8E3BF, 0xBFEBFBFF}},
+ {{0x00000002, 0}, Leaf{0x61B3A001, 0x0000FFC2, 0x00000000, 0x00000000}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C000121, 0x0140003F, 0x0000003F, 0x00000001}},
+ {{0x00000004, 1}, Leaf{0x1C000122, 0x01C0003F, 0x0000003F, 0x00000001}},
+ {{0x00000004, 2}, Leaf{0x1C00C143, 0x03C0003F, 0x000003FF, 0x00000001}},
+ {{0x00000005, 0}, Leaf{0x00000040, 0x00000040, 0x00000003, 0x33000020}},
+ {{0x00000006, 0}, Leaf{0x00000005, 0x00000002, 0x00000009, 0x00000000}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00002282, 0x00000000, 0x00000000}},
+ {{0x00000008, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000009, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x0000000A, 0}, Leaf{0x07280203, 0x00000000, 0x00000000, 0x00004503}},
+ {{0x0000000B, 0}, Leaf{0x00000001, 0x00000001, 0x00000100, 0x00000000}},
+ {{0x0000000B, 1}, Leaf{0x00000004, 0x00000004, 0x00000201, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000101, 0x28100000}},
+ {{0x80000002, 0}, Leaf{0x20202020, 0x6E492020, 0x286C6574, 0x43202952}},
+ {{0x80000003, 0}, Leaf{0x72656C65, 0x52286E6F, 0x50432029, 0x4A202055}},
+ {{0x80000004, 0}, Leaf{0x30303931, 0x20402020, 0x39392E31, 0x007A4847}},
+ {{0x80000005, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000006, 0}, Leaf{0x00000000, 0x00000000, 0x04008040, 0x00000000}},
+ {{0x80000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000100}},
+ {{0x80000008, 0}, Leaf{0x00003024, 0x00000000, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x37);
+ EXPECT_EQ(info.stepping, 0x03);
+ EXPECT_EQ(GetX86Microarchitecture(&info),
+ X86Microarchitecture::INTEL_ATOM_SMT);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, " Intel(R) Celeron(R) CPU J1900 @ 1.99GHz");
+
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_TRUE(info.features.sse2);
+ EXPECT_TRUE(info.features.sse3);
+#ifndef 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
+}
+
+// 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);
+#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)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(
+flags : fpu mmx sse
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000003, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x00000673, 0x00000000, 0x00000000, 0x0387FBFF}},
+ {{0x00000002, 0}, Leaf{0x03020101, 0x00000000, 0x00000000, 0x0C040843}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x4CECC782, 0x00006778}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x07);
+ EXPECT_EQ(info.stepping, 0x03);
+ EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::X86_UNKNOWN);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "");
+
+ EXPECT_TRUE(info.features.mmx);
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_FALSE(info.features.sse2);
+ EXPECT_FALSE(info.features.sse3);
+#ifndef 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
+}
+
+// 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.
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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 "filesystem_for_testing.h"
+
+#include <cassert>
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+namespace cpu_features {
+
+FakeFile::FakeFile(int file_descriptor, const char* content)
+ : file_descriptor_(file_descriptor), content_(content) {}
+
+FakeFile::~FakeFile() { assert(!opened_); }
+
+void FakeFile::Open() {
+ assert(!opened_);
+ opened_ = true;
+}
+
+void FakeFile::Close() {
+ assert(opened_);
+ opened_ = false;
+}
+
+int FakeFile::Read(int fd, void* buf, size_t count) {
+ assert(count < INT_MAX);
+ assert(fd == file_descriptor_);
+ const size_t remainder = content_.size() - head_index_;
+ const size_t read = count > remainder ? remainder : count;
+ memcpy(buf, content_.data() + head_index_, read);
+ head_index_ += read;
+ assert(read < INT_MAX);
+ return (int)read;
+}
+
+void FakeFilesystem::Reset() { files_.clear(); }
+
+FakeFile* FakeFilesystem::CreateFile(const std::string& filename,
+ const char* content) {
+ auto& file = files_[filename];
+ file =
+ std::unique_ptr<FakeFile>(new FakeFile(next_file_descriptor_++, content));
+ return file.get();
+}
+
+FakeFile* FakeFilesystem::FindFileOrNull(const std::string& filename) const {
+ const auto itr = files_.find(filename);
+ return itr == files_.end() ? nullptr : itr->second.get();
+}
+
+FakeFile* FakeFilesystem::FindFileOrDie(const int file_descriptor) const {
+ for (const auto& filename_file_pair : files_) {
+ FakeFile* const file_ptr = filename_file_pair.second.get();
+ if (file_ptr->GetFileDescriptor() == file_descriptor) {
+ return file_ptr;
+ }
+ }
+ assert(false);
+ return nullptr;
+}
+
+static FakeFilesystem* kFilesystem = new FakeFilesystem();
+
+FakeFilesystem& GetEmptyFilesystem() {
+ kFilesystem->Reset();
+ return *kFilesystem;
+}
+
+extern "C" int CpuFeatures_OpenFile(const char* filename) {
+ auto* const file = kFilesystem->FindFileOrNull(filename);
+ if (file) {
+ file->Open();
+ return file->GetFileDescriptor();
+ }
+ return -1;
+}
+
+extern "C" void CpuFeatures_CloseFile(int file_descriptor) {
+ kFilesystem->FindFileOrDie(file_descriptor)->Close();
+}
+
+extern "C" int CpuFeatures_ReadFile(int file_descriptor, void* buffer,
+ size_t buffer_size) {
+ return kFilesystem->FindFileOrDie(file_descriptor)
+ ->Read(file_descriptor, buffer, buffer_size);
+}
+
+} // namespace cpu_features
--- /dev/null
+// 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.
+
+// Implements a fake filesystem, useful for tests.
+#ifndef CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
+#define CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "internal/filesystem.h"
+
+namespace cpu_features {
+
+class FakeFile {
+ public:
+ explicit FakeFile(int file_descriptor, const char* content);
+ ~FakeFile();
+
+ void Open();
+ void Close();
+ int Read(int fd, void* buf, size_t count);
+
+ int GetFileDescriptor() const { return file_descriptor_; }
+
+ private:
+ const int file_descriptor_;
+ const std::string content_;
+ bool opened_ = false;
+ size_t head_index_ = 0;
+};
+
+class FakeFilesystem {
+ public:
+ void Reset();
+ FakeFile* CreateFile(const std::string& filename, const char* content);
+ FakeFile* FindFileOrDie(const int file_descriptor) const;
+ FakeFile* FindFileOrNull(const std::string& filename) const;
+
+ private:
+ int next_file_descriptor_ = 0;
+ std::unordered_map<std::string, std::unique_ptr<FakeFile>> files_;
+};
+
+FakeFilesystem& GetEmptyFilesystem();
+
+} // namespace cpu_features
+
+#endif // CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
--- /dev/null
+// 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 "hwcaps_for_testing.h"
+
+#include <string.h>
+
+#include "internal/string_view.h"
+
+namespace cpu_features {
+
+namespace {
+static auto* const g_hardware_capabilities = new HardwareCapabilities();
+static auto* const g_platform_types = new PlatformType();
+} // namespace
+
+void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2) {
+ g_hardware_capabilities->hwcaps = hwcaps;
+ g_hardware_capabilities->hwcaps2 = hwcaps2;
+}
+
+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));
+}
+
+PlatformType CpuFeatures_GetPlatformType(void) { return *g_platform_types; }
+} // namespace cpu_features
--- /dev/null
+// 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 CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
+#define CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
+
+#include "internal/hwcaps.h"
+
+namespace cpu_features {
+
+void SetHardwareCapabilities(uint32_t hwcaps, uint32_t hwcaps2);
+void SetPlatformTypes(const char *platform, const char *base_platform);
+
+} // namespace cpu_features
+
+#endif // CPU_FEATURES_TEST_HWCAPS_FOR_TESTING_H_
--- /dev/null
+// 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 "internal/stack_line_reader.h"
+
+#include "filesystem_for_testing.h"
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+bool operator==(const StringView& a, const StringView& b) {
+ return CpuFeatures_StringView_IsEquals(a, b);
+}
+
+namespace {
+
+std::string ToString(StringView view) { return {view.ptr, view.size}; }
+
+TEST(StackLineReaderTest, Empty) {
+ auto& fs = GetEmptyFilesystem();
+ auto* file = fs.CreateFile("/proc/cpuinfo", "");
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_TRUE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str(""));
+ }
+}
+
+TEST(StackLineReaderTest, ManySmallLines) {
+ auto& fs = GetEmptyFilesystem();
+ auto* file = fs.CreateFile("/proc/cpuinfo", "a\nb\nc");
+
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("a"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("b"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_TRUE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("c"));
+ }
+}
+
+TEST(StackLineReaderTest, TruncatedLine) {
+ auto& fs = GetEmptyFilesystem();
+ auto* file = fs.CreateFile("/proc/cpuinfo", R"(First
+Second
+More than 16 characters, this will be truncated.
+last)");
+
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("First"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("Second"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_FALSE(result.full_line);
+ EXPECT_EQ(result.line, str("More than 16 cha"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_TRUE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str("last"));
+ }
+}
+
+TEST(StackLineReaderTest, TruncatedLines) {
+ auto& fs = GetEmptyFilesystem();
+ auto* file = fs.CreateFile("/proc/cpuinfo", R"(More than 16 characters
+Another line that is too long)");
+
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, file->GetFileDescriptor());
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_FALSE(result.full_line);
+ EXPECT_EQ(result.line, str("More than 16 cha"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_FALSE(result.eof);
+ EXPECT_FALSE(result.full_line);
+ EXPECT_EQ(result.line, str("Another line tha"));
+ }
+ {
+ const auto result = StackLineReader_NextLine(&reader);
+ EXPECT_TRUE(result.eof);
+ EXPECT_TRUE(result.full_line);
+ EXPECT_EQ(result.line, str(""));
+ }
+}
+
+} // namespace
+} // namespace cpu_features
--- /dev/null
+// 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 "internal/string_view.h"
+
+#include "gtest/gtest.h"
+
+namespace cpu_features {
+
+bool operator==(const StringView& a, const StringView& b) {
+ return CpuFeatures_StringView_IsEquals(a, b);
+}
+
+namespace {
+
+TEST(StringViewTest, Empty) {
+ EXPECT_EQ(kEmptyStringView.ptr, nullptr);
+ EXPECT_EQ(kEmptyStringView.size, 0);
+}
+
+TEST(StringViewTest, Build) {
+ const auto view = str("test");
+ EXPECT_EQ(view.ptr[0], 't');
+ EXPECT_EQ(view.size, 4);
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_IndexOfChar) {
+ // Found.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOfChar(str("test"), 'e'), 1);
+ EXPECT_EQ(CpuFeatures_StringView_IndexOfChar(str("test"), 't'), 0);
+ EXPECT_EQ(CpuFeatures_StringView_IndexOfChar(str("beef"), 'e'), 1);
+ // Not found.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOfChar(str("test"), 'z'), -1);
+ // Empty.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOfChar(kEmptyStringView, 'z'), -1);
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_IndexOf) {
+ // Found.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(str("test"), str("es")), 1);
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(str("test"), str("test")), 0);
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(str("tesstest"), str("test")), 4);
+ // Not found.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(str("test"), str("aa")), -1);
+ // Empty.
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(kEmptyStringView, str("aa")), -1);
+ EXPECT_EQ(CpuFeatures_StringView_IndexOf(str("aa"), kEmptyStringView), -1);
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_StartsWith) {
+ EXPECT_TRUE(CpuFeatures_StringView_StartsWith(str("test"), str("te")));
+ EXPECT_TRUE(CpuFeatures_StringView_StartsWith(str("test"), str("test")));
+ EXPECT_FALSE(CpuFeatures_StringView_StartsWith(str("test"), str("st")));
+ EXPECT_FALSE(CpuFeatures_StringView_StartsWith(str("test"), str("est")));
+ EXPECT_FALSE(CpuFeatures_StringView_StartsWith(str("test"), str("")));
+ EXPECT_FALSE(
+ CpuFeatures_StringView_StartsWith(str("test"), kEmptyStringView));
+ EXPECT_FALSE(
+ CpuFeatures_StringView_StartsWith(kEmptyStringView, str("test")));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_IsEquals) {
+ EXPECT_TRUE(
+ CpuFeatures_StringView_IsEquals(kEmptyStringView, kEmptyStringView));
+ EXPECT_TRUE(CpuFeatures_StringView_IsEquals(kEmptyStringView, str("")));
+ EXPECT_TRUE(CpuFeatures_StringView_IsEquals(str(""), kEmptyStringView));
+ EXPECT_TRUE(CpuFeatures_StringView_IsEquals(str("test"), str("test")));
+ EXPECT_TRUE(CpuFeatures_StringView_IsEquals(str("a"), str("a")));
+ EXPECT_FALSE(CpuFeatures_StringView_IsEquals(str("a"), str("b")));
+ EXPECT_FALSE(CpuFeatures_StringView_IsEquals(str("aa"), str("a")));
+ EXPECT_FALSE(CpuFeatures_StringView_IsEquals(str("a"), str("aa")));
+ EXPECT_FALSE(CpuFeatures_StringView_IsEquals(str("a"), kEmptyStringView));
+ EXPECT_FALSE(CpuFeatures_StringView_IsEquals(kEmptyStringView, str("a")));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_PopFront) {
+ EXPECT_EQ(CpuFeatures_StringView_PopFront(str("test"), 2), str("st"));
+ EXPECT_EQ(CpuFeatures_StringView_PopFront(str("test"), 0), str("test"));
+ EXPECT_EQ(CpuFeatures_StringView_PopFront(str("test"), 4), str(""));
+ EXPECT_EQ(CpuFeatures_StringView_PopFront(str("test"), 100), str(""));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_PopBack) {
+ EXPECT_EQ(CpuFeatures_StringView_PopBack(str("test"), 2), str("te"));
+ EXPECT_EQ(CpuFeatures_StringView_PopBack(str("test"), 0), str("test"));
+ EXPECT_EQ(CpuFeatures_StringView_PopBack(str("test"), 4), str(""));
+ EXPECT_EQ(CpuFeatures_StringView_PopBack(str("test"), 100), str(""));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_KeepFront) {
+ EXPECT_EQ(CpuFeatures_StringView_KeepFront(str("test"), 2), str("te"));
+ EXPECT_EQ(CpuFeatures_StringView_KeepFront(str("test"), 0), str(""));
+ EXPECT_EQ(CpuFeatures_StringView_KeepFront(str("test"), 4), str("test"));
+ EXPECT_EQ(CpuFeatures_StringView_KeepFront(str("test"), 6), str("test"));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_Front) {
+ EXPECT_EQ(CpuFeatures_StringView_Front(str("apple")), 'a');
+ EXPECT_EQ(CpuFeatures_StringView_Front(str("a")), 'a');
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_Back) {
+ EXPECT_EQ(CpuFeatures_StringView_Back(str("apple")), 'e');
+ EXPECT_EQ(CpuFeatures_StringView_Back(str("a")), 'a');
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_TrimWhitespace) {
+ EXPECT_EQ(CpuFeatures_StringView_TrimWhitespace(str(" first middle last ")),
+ str("first middle last"));
+ EXPECT_EQ(CpuFeatures_StringView_TrimWhitespace(str("first middle last ")),
+ str("first middle last"));
+ EXPECT_EQ(CpuFeatures_StringView_TrimWhitespace(str(" first middle last")),
+ str("first middle last"));
+ EXPECT_EQ(CpuFeatures_StringView_TrimWhitespace(str("first middle last")),
+ str("first middle last"));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_ParsePositiveNumber) {
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("42")), 42);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("0x2a")), 42);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("0x2A")), 42);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("0x2A2a")), 10794);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("0x2a2A")), 10794);
+
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("-10")), -1);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("-0x2A")), -1);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("abc")), -1);
+ EXPECT_EQ(CpuFeatures_StringView_ParsePositiveNumber(str("")), -1);
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_CopyString) {
+ char buf[4];
+ buf[0] = 'X';
+
+ // Empty
+ CpuFeatures_StringView_CopyString(str(""), buf, sizeof(buf));
+ EXPECT_STREQ(buf, "");
+
+ // Less
+ CpuFeatures_StringView_CopyString(str("a"), buf, sizeof(buf));
+ EXPECT_STREQ(buf, "a");
+
+ // exact
+ CpuFeatures_StringView_CopyString(str("abc"), buf, sizeof(buf));
+ EXPECT_STREQ(buf, "abc");
+
+ // More
+ CpuFeatures_StringView_CopyString(str("abcd"), buf, sizeof(buf));
+ EXPECT_STREQ(buf, "abc");
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_HasWord) {
+ // Find flags at beginning, middle and end.
+ 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"));
+}
+
+TEST(StringViewTest, CpuFeatures_StringView_GetAttributeKeyValue) {
+ const StringView line = str(" key : first middle last ");
+ StringView key, value;
+ EXPECT_TRUE(CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value));
+ EXPECT_EQ(key, str("key"));
+ EXPECT_EQ(value, str("first middle last"));
+}
+
+TEST(StringViewTest, FailingGetAttributeKeyValue) {
+ const StringView line = str("key first middle last");
+ StringView key, value;
+ EXPECT_FALSE(CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value));
+}
+
+} // namespace
+} // namespace cpu_features