Add work towards using cpu_features. Keep compatibility with old systems

This commit is contained in:
Carles Fernandez 2020-08-21 14:43:04 +02:00
parent ec4c4b7ee4
commit cda2c998b8
No known key found for this signature in database
GPG Key ID: 4C583C52B0C3877D
61 changed files with 8795 additions and 273 deletions

208
LICENSES/Apache-2.0.txt Normal file
View File

@ -0,0 +1,208 @@
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.

View File

@ -8,3 +8,4 @@ build/
.project
.cproject
/.DS_Store
/gen/volk_gnsssdr_arch_defs.py

View File

@ -227,6 +227,15 @@ endif()
# Dependencies setup
########################################################################
# cpu_features
if(CMAKE_VERSION VERSION_GREATER 3.0)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(BUILD_PIC ON)
set(USE_CPU_FEATURES ON)
add_subdirectory(cpu_features)
endif()
set(USE_CPU_FEATURES OFF)
# Python
include(VolkPython) # sets PYTHON_EXECUTABLE
volk_python_check_module("python >= 2.7" sys "sys.version.split()[0] >= '2.7'" PYTHON_MIN_VER_FOUND)

View File

@ -0,0 +1,260 @@
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
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.1.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()
# 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}/include/internal/unix_features_aggregator.h
${PROJECT_SOURCE_DIR}/src/hwcaps.c
${PROJECT_SOURCE_DIR}/src/unix_features_aggregator.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>
)
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
)

View File

@ -0,0 +1,192 @@
# cpu_features
<!-- prettier-ignore-start -->
[comment]: # (
SPDX-License-Identifier: Apache-2.0
)
[comment]: # (
SPDX-FileCopyrightText: 2017 Google Inc.
)
<!-- prettier-ignore-end -->
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>
### 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"
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"
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"
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&mdash;but only if it's not Sandy Bridge.
```c
#include <stdbool.h>
#include "cpuinfo_x86.h"
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` 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).

View File

@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
# CpuFeatures CMake configuration file
include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesTargets.cmake")

View File

@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
# CpuFeaturesNdkCompat CMake configuration file
include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesNdkCompatTargets.cmake")

View File

@ -0,0 +1,37 @@
# CMake build instructions
<!-- prettier-ignore-start -->
[comment]: # (
SPDX-License-Identifier: Apache-2.0
)
[comment]: # (
SPDX-FileCopyrightText: 2017 Google Inc.
)
<!-- prettier-ignore-end -->
## 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.

View File

@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
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 ""
)

View File

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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_

View File

@ -0,0 +1,204 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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
////////////////////////////////////////////////////////////////////////////////
// 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_

View File

@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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.
// 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_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_

View File

@ -0,0 +1,110 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
#include <stdint.h> // uint32_t
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_

View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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_

View File

@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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_

View File

@ -0,0 +1,208 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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 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
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_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_

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#ifndef CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
#define CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
#include "cpu_features_macros.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.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_

View File

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#ifndef CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
#define CPU_FEATURES_INCLUDE_INTERNAL_CPUID_X86_H_
#include "cpu_features_macros.h"
#include <stdint.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;
Leaf CpuIdEx(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_

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// 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 "cpu_features_macros.h"
#include <stddef.h>
#include <stdint.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_

View File

@ -0,0 +1,155 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// 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 "cpu_features_macros.h"
#include <stdint.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)
// 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);
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_

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// 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 "cpu_features_macros.h"
#include "internal/string_view.h"
#include <stdbool.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_

View File

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// 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 "cpu_features_macros.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.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_

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// CapabilityConfig provides a way to map cpu features to hardware caps and
// /proc/cpuinfo flags. We then provide functions to update capabilities from
// either source.
#ifndef CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_
#define CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_
#include "cpu_features_macros.h"
#include "internal/hwcaps.h"
#include "internal/string_view.h"
#include <ctype.h>
#include <stdint.h>
CPU_FEATURES_START_CPP_NAMESPACE
// Use the following macro to declare setter functions to be used in
// CapabilityConfig.
#define DECLARE_SETTER(FeatureType, FeatureName) \
static void set_##FeatureName(void* const features, bool value) \
{ \
((FeatureType*)features)->FeatureName = value; \
}
// Use the following macro to declare getter functions to be used in
// CapabilityConfig.
#define DECLARE_GETTER(FeatureType, FeatureName) \
static int get_##FeatureName(void* const features) \
{ \
return ((FeatureType*)features)->FeatureName; \
}
#define DECLARE_SETTER_AND_GETTER(FeatureType, FeatureName) \
DECLARE_SETTER(FeatureType, FeatureName) \
DECLARE_GETTER(FeatureType, FeatureName)
// Describes the relationship between hardware caps and /proc/cpuinfo flags.
typedef struct
{
const HardwareCapabilities hwcaps_mask;
const char* const proc_cpuinfo_flag;
void (*set_bit)(void* const, bool); // setter for the corresponding bit.
int (*get_bit)(void* const); // getter for the corresponding bit.
} CapabilityConfig;
// For every config, looks into flags_line for the presence of the
// corresponding proc_cpuinfo_flag, calls `set_bit` accordingly.
// Note: features is a pointer to the underlying Feature struct.
void CpuFeatures_SetFromFlags(const size_t configs_size,
const CapabilityConfig* configs,
const StringView flags_line,
void* const features);
// For every config, looks into hwcaps for the presence of the feature. Calls
// `set_bit` with true if the hardware capability is found.
// Note: features is a pointer to the underlying Feature struct.
void CpuFeatures_OverrideFromHwCaps(const size_t configs_size,
const CapabilityConfig* configs,
const HardwareCapabilities hwcaps,
void* const features);
CPU_FEATURES_END_CPP_NAMESPACE
#endif // CPU_FEATURES_INCLUDE_INTERNAL_LINUX_FEATURES_AGGREGATOR_H_

View File

@ -0,0 +1,61 @@
# SPDX-FileCopyrightText: 2010 The Android Open Source Project
# SPDX-License-Identifier: BSD-2-Clause
#
# 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_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()

View File

@ -0,0 +1,16 @@
<!-- prettier-ignore-start -->
[comment]: # (
SPDX-License-Identifier: BSD-2-Clause
)
[comment]: # (
SPDX-FileCopyrightText: 2010 The Android Open Source Project
)
<!-- prettier-ignore-end -->
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).

View File

@ -0,0 +1,231 @@
/*
* SPDX-FileCopyrightText: 2010 The Android Open Source Project
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "cpu-features.h"
#include "cpu_features_macros.h"
#include "internal/filesystem.h"
#include "internal/stack_line_reader.h"
#include "internal/string_view.h"
#include <pthread.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

View File

@ -0,0 +1,303 @@
/*
* SPDX-FileCopyrightText: 2010 The Android Open Source Project
* SPDX-License-Identifier: BSD-2-Clause
*/
#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 */

View File

@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2010 The Android Open Source Project
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "cpu-features.h"
#include <stdio.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__
}

View File

@ -0,0 +1,212 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
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

View File

@ -0,0 +1,109 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
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

View File

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "cpuinfo_aarch64.h"
#include "internal/filesystem.h"
#include "internal/hwcaps.h"
#include "internal/stack_line_reader.h"
#include "internal/string_view.h"
#include "internal/unix_features_aggregator.h"
#include <assert.h>
#include <ctype.h>
DECLARE_SETTER_AND_GETTER(Aarch64Features, fp)
DECLARE_SETTER_AND_GETTER(Aarch64Features, asimd)
DECLARE_SETTER_AND_GETTER(Aarch64Features, evtstrm)
DECLARE_SETTER_AND_GETTER(Aarch64Features, aes)
DECLARE_SETTER_AND_GETTER(Aarch64Features, pmull)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sha1)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sha2)
DECLARE_SETTER_AND_GETTER(Aarch64Features, crc32)
DECLARE_SETTER_AND_GETTER(Aarch64Features, atomics)
DECLARE_SETTER_AND_GETTER(Aarch64Features, fphp)
DECLARE_SETTER_AND_GETTER(Aarch64Features, asimdhp)
DECLARE_SETTER_AND_GETTER(Aarch64Features, cpuid)
DECLARE_SETTER_AND_GETTER(Aarch64Features, asimdrdm)
DECLARE_SETTER_AND_GETTER(Aarch64Features, jscvt)
DECLARE_SETTER_AND_GETTER(Aarch64Features, fcma)
DECLARE_SETTER_AND_GETTER(Aarch64Features, lrcpc)
DECLARE_SETTER_AND_GETTER(Aarch64Features, dcpop)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sha3)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sm3)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sm4)
DECLARE_SETTER_AND_GETTER(Aarch64Features, asimddp)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sha512)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sve)
DECLARE_SETTER_AND_GETTER(Aarch64Features, asimdfhm)
DECLARE_SETTER_AND_GETTER(Aarch64Features, dit)
DECLARE_SETTER_AND_GETTER(Aarch64Features, uscat)
DECLARE_SETTER_AND_GETTER(Aarch64Features, ilrcpc)
DECLARE_SETTER_AND_GETTER(Aarch64Features, flagm)
DECLARE_SETTER_AND_GETTER(Aarch64Features, ssbs)
DECLARE_SETTER_AND_GETTER(Aarch64Features, sb)
DECLARE_SETTER_AND_GETTER(Aarch64Features, paca)
DECLARE_SETTER_AND_GETTER(Aarch64Features, pacg)
static const CapabilityConfig kConfigs[] = {
[AARCH64_FP] = {{AARCH64_HWCAP_FP, 0}, "fp", &set_fp, &get_fp},
[AARCH64_ASIMD] = {{AARCH64_HWCAP_ASIMD, 0}, "asimd", &set_asimd, &get_asimd},
[AARCH64_EVTSTRM] = {{AARCH64_HWCAP_EVTSTRM, 0}, "evtstrm", &set_evtstrm, &get_evtstrm},
[AARCH64_AES] = {{AARCH64_HWCAP_AES, 0}, "aes", &set_aes, &get_aes},
[AARCH64_PMULL] = {{AARCH64_HWCAP_PMULL, 0}, "pmull", &set_pmull, &get_pmull},
[AARCH64_SHA1] = {{AARCH64_HWCAP_SHA1, 0}, "sha1", &set_sha1, &get_sha1},
[AARCH64_SHA2] = {{AARCH64_HWCAP_SHA2, 0}, "sha2", &set_sha2, &get_sha2},
[AARCH64_CRC32] = {{AARCH64_HWCAP_CRC32, 0}, "crc32", &set_crc32, &get_crc32},
[AARCH64_ATOMICS] = {{AARCH64_HWCAP_ATOMICS, 0}, "atomics", &set_atomics, &get_atomics},
[AARCH64_FPHP] = {{AARCH64_HWCAP_FPHP, 0}, "fphp", &set_fphp, &get_fphp},
[AARCH64_ASIMDHP] = {{AARCH64_HWCAP_ASIMDHP, 0}, "asimdhp", &set_asimdhp, &get_asimdhp},
[AARCH64_CPUID] = {{AARCH64_HWCAP_CPUID, 0}, "cpuid", &set_cpuid, &get_cpuid},
[AARCH64_ASIMDRDM] = {{AARCH64_HWCAP_ASIMDRDM, 0}, "asimdrdm", &set_asimdrdm, &get_asimdrdm},
[AARCH64_JSCVT] = {{AARCH64_HWCAP_JSCVT, 0}, "jscvt", &set_jscvt, &get_jscvt},
[AARCH64_FCMA] = {{AARCH64_HWCAP_FCMA, 0}, "fcma", &set_fcma, &get_fcma},
[AARCH64_LRCPC] = {{AARCH64_HWCAP_LRCPC, 0}, "lrcpc", &set_lrcpc, &get_lrcpc},
[AARCH64_DCPOP] = {{AARCH64_HWCAP_DCPOP, 0}, "dcpop", &set_dcpop, &get_dcpop},
[AARCH64_SHA3] = {{AARCH64_HWCAP_SHA3, 0}, "sha3", &set_sha3, &get_sha3},
[AARCH64_SM3] = {{AARCH64_HWCAP_SM3, 0}, "sm3", &set_sm3, &get_sm3},
[AARCH64_SM4] = {{AARCH64_HWCAP_SM4, 0}, "sm4", &set_sm4, &get_sm4},
[AARCH64_ASIMDDP] = {{AARCH64_HWCAP_ASIMDDP, 0}, "asimddp", &set_asimddp, &get_asimddp},
[AARCH64_SHA512] = {{AARCH64_HWCAP_SHA512, 0}, "sha512", &set_sha512, &get_sha512},
[AARCH64_SVE] = {{AARCH64_HWCAP_SVE, 0}, "sve", &set_sve, &get_sve},
[AARCH64_ASIMDFHM] = {{AARCH64_HWCAP_ASIMDFHM, 0}, "asimdfhm", &set_asimdfhm, &get_asimdfhm},
[AARCH64_DIT] = {{AARCH64_HWCAP_DIT, 0}, "dit", &set_dit, &get_dit},
[AARCH64_USCAT] = {{AARCH64_HWCAP_USCAT, 0}, "uscat", &set_uscat, &get_uscat},
[AARCH64_ILRCPC] = {{AARCH64_HWCAP_ILRCPC, 0}, "ilrcpc", &set_ilrcpc, &get_ilrcpc},
[AARCH64_FLAGM] = {{AARCH64_HWCAP_FLAGM, 0}, "flagm", &set_flagm, &get_flagm},
[AARCH64_SSBS] = {{AARCH64_HWCAP_SSBS, 0}, "ssbs", &set_ssbs, &get_ssbs},
[AARCH64_SB] = {{AARCH64_HWCAP_SB, 0}, "sb", &set_sb, &get_sb},
[AARCH64_PACA] = {{AARCH64_HWCAP_PACA, 0}, "paca", &set_paca, &get_paca},
[AARCH64_PACG] = {{AARCH64_HWCAP_PACG, 0}, "pacg", &set_pacg, &get_pacg},
};
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
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")))
{
CpuFeatures_SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
}
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)
{
assert(kConfigsSize == AARCH64_LAST_);
// 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);
CpuFeatures_OverrideFromHwCaps(kConfigsSize, kConfigs,
CpuFeatures_GetHardwareCapabilities(),
&info.features);
return info;
}
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
Aarch64FeaturesEnum value)
{
if (value >= kConfigsSize)
return false;
return kConfigs[value].get_bit((Aarch64Features*)features);
}
const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum value)
{
if (value >= kConfigsSize)
return "unknown feature";
return kConfigs[value].proc_cpuinfo_flag;
}

View File

@ -0,0 +1,254 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "cpuinfo_arm.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"
#include "internal/unix_features_aggregator.h"
#include <assert.h>
#include <ctype.h>
DECLARE_SETTER_AND_GETTER(ArmFeatures, swp)
DECLARE_SETTER_AND_GETTER(ArmFeatures, half)
DECLARE_SETTER_AND_GETTER(ArmFeatures, thumb)
DECLARE_SETTER_AND_GETTER(ArmFeatures, _26bit)
DECLARE_SETTER_AND_GETTER(ArmFeatures, fastmult)
DECLARE_SETTER_AND_GETTER(ArmFeatures, fpa)
DECLARE_SETTER_AND_GETTER(ArmFeatures, vfp)
DECLARE_SETTER_AND_GETTER(ArmFeatures, edsp)
DECLARE_SETTER_AND_GETTER(ArmFeatures, java)
DECLARE_SETTER_AND_GETTER(ArmFeatures, iwmmxt)
DECLARE_SETTER_AND_GETTER(ArmFeatures, crunch)
DECLARE_SETTER_AND_GETTER(ArmFeatures, thumbee)
DECLARE_SETTER_AND_GETTER(ArmFeatures, neon)
DECLARE_SETTER_AND_GETTER(ArmFeatures, vfpv3)
DECLARE_SETTER_AND_GETTER(ArmFeatures, vfpv3d16)
DECLARE_SETTER_AND_GETTER(ArmFeatures, tls)
DECLARE_SETTER_AND_GETTER(ArmFeatures, vfpv4)
DECLARE_SETTER_AND_GETTER(ArmFeatures, idiva)
DECLARE_SETTER_AND_GETTER(ArmFeatures, idivt)
DECLARE_SETTER_AND_GETTER(ArmFeatures, vfpd32)
DECLARE_SETTER_AND_GETTER(ArmFeatures, lpae)
DECLARE_SETTER_AND_GETTER(ArmFeatures, evtstrm)
DECLARE_SETTER_AND_GETTER(ArmFeatures, aes)
DECLARE_SETTER_AND_GETTER(ArmFeatures, pmull)
DECLARE_SETTER_AND_GETTER(ArmFeatures, sha1)
DECLARE_SETTER_AND_GETTER(ArmFeatures, sha2)
DECLARE_SETTER_AND_GETTER(ArmFeatures, crc32)
static const CapabilityConfig kConfigs[] = {
[ARM_SWP] = {{ARM_HWCAP_SWP, 0}, "swp", &set_swp, &get_swp}, //
[ARM_HALF] = {{ARM_HWCAP_HALF, 0}, "half", &set_half, &get_half}, //
[ARM_THUMB] = {{ARM_HWCAP_THUMB, 0}, "thumb", &set_thumb, &get_thumb}, //
[ARM_26BIT] = {{ARM_HWCAP_26BIT, 0}, "26bit", &set__26bit, &get__26bit}, //
[ARM_FASTMULT] = {{ARM_HWCAP_FAST_MULT, 0}, "fastmult", &set_fastmult, &get_fastmult}, //
[ARM_FPA] = {{ARM_HWCAP_FPA, 0}, "fpa", &set_fpa, &get_fpa}, //
[ARM_VFP] = {{ARM_HWCAP_VFP, 0}, "vfp", &set_vfp, &get_vfp}, //
[ARM_EDSP] = {{ARM_HWCAP_EDSP, 0}, "edsp", &set_edsp, &get_edsp}, //
[ARM_JAVA] = {{ARM_HWCAP_JAVA, 0}, "java", &set_java, &get_java}, //
[ARM_IWMMXT] = {{ARM_HWCAP_IWMMXT, 0}, "iwmmxt", &set_iwmmxt, &get_iwmmxt}, //
[ARM_CRUNCH] = {{ARM_HWCAP_CRUNCH, 0}, "crunch", &set_crunch, &get_crunch}, //
[ARM_THUMBEE] = {{ARM_HWCAP_THUMBEE, 0}, "thumbee", &set_thumbee, &get_thumbee}, //
[ARM_NEON] = {{ARM_HWCAP_NEON, 0}, "neon", &set_neon, &get_neon}, //
[ARM_VFPV3] = {{ARM_HWCAP_VFPV3, 0}, "vfpv3", &set_vfpv3, &get_vfpv3}, //
[ARM_VFPV3D16] = {{ARM_HWCAP_VFPV3D16, 0}, "vfpv3d16", &set_vfpv3d16, &get_vfpv3d16}, //
[ARM_TLS] = {{ARM_HWCAP_TLS, 0}, "tls", &set_tls, &get_tls}, //
[ARM_VFPV4] = {{ARM_HWCAP_VFPV4, 0}, "vfpv4", &set_vfpv4, &get_vfpv4}, //
[ARM_IDIVA] = {{ARM_HWCAP_IDIVA, 0}, "idiva", &set_idiva, &get_idiva}, //
[ARM_IDIVT] = {{ARM_HWCAP_IDIVT, 0}, "idivt", &set_idivt, &get_idivt}, //
[ARM_VFPD32] = {{ARM_HWCAP_VFPD32, 0}, "vfpd32", &set_vfpd32, &get_vfpd32}, //
[ARM_LPAE] = {{ARM_HWCAP_LPAE, 0}, "lpae", &set_lpae, &get_lpae}, //
[ARM_EVTSTRM] = {{ARM_HWCAP_EVTSTRM, 0}, "evtstrm", &set_evtstrm, &get_evtstrm}, //
[ARM_AES] = {{0, ARM_HWCAP2_AES}, "aes", &set_aes, &get_aes}, //
[ARM_PMULL] = {{0, ARM_HWCAP2_PMULL}, "pmull", &set_pmull, &get_pmull}, //
[ARM_SHA1] = {{0, ARM_HWCAP2_SHA1}, "sha1", &set_sha1, &get_sha1}, //
[ARM_SHA2] = {{0, ARM_HWCAP2_SHA2}, "sha2", &set_sha2, &get_sha2}, //
[ARM_CRC32] = {{0, ARM_HWCAP2_CRC32}, "crc32", &set_crc32, &get_crc32}, //
};
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
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")))
{
CpuFeatures_SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
}
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);
CpuFeatures_OverrideFromHwCaps(kConfigsSize, kConfigs,
CpuFeatures_GetHardwareCapabilities(),
&info.features);
FixErrors(&info, &proc_cpu_info_data);
return info;
}
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
int GetArmFeaturesEnumValue(const ArmFeatures* features,
ArmFeaturesEnum value)
{
if (value >= kConfigsSize)
return false;
return kConfigs[value].get_bit((ArmFeatures*)features);
}
const char* GetArmFeaturesEnumName(ArmFeaturesEnum value)
{
if (value >= kConfigsSize)
return "unknown feature";
return kConfigs[value].proc_cpuinfo_flag;
}

View File

@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "cpuinfo_mips.h"
#include "internal/filesystem.h"
#include "internal/stack_line_reader.h"
#include "internal/string_view.h"
#include "internal/unix_features_aggregator.h"
#include <assert.h>
DECLARE_SETTER_AND_GETTER(MipsFeatures, msa)
DECLARE_SETTER_AND_GETTER(MipsFeatures, eva)
DECLARE_SETTER_AND_GETTER(MipsFeatures, r6)
static const CapabilityConfig kConfigs[] = {
[MIPS_MSA] = {{MIPS_HWCAP_MSA, 0}, "msa", &set_msa, &get_msa}, //
[MIPS_EVA] = {{0, 0}, "eva", &set_eva, &get_eva}, //
[MIPS_R6] = {{MIPS_HWCAP_R6, 0}, "r6", &set_r6, &get_r6}, //
};
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
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")))
{
CpuFeatures_SetFromFlags(kConfigsSize, kConfigs, value, features);
}
}
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)
{
assert(kConfigsSize == MIPS_LAST_);
// 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);
CpuFeatures_OverrideFromHwCaps(kConfigsSize, kConfigs,
CpuFeatures_GetHardwareCapabilities(),
&info.features);
return info;
}
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
int GetMipsFeaturesEnumValue(const MipsFeatures* features,
MipsFeaturesEnum value)
{
if (value >= kConfigsSize)
return false;
return kConfigs[value].get_bit((MipsFeatures*)features);
}
const char* GetMipsFeaturesEnumName(MipsFeaturesEnum value)
{
if (value >= kConfigsSize)
return "unknown feature";
return kConfigs[value].proc_cpuinfo_flag;
}

View File

@ -0,0 +1,195 @@
// SPDX-FileCopyrightText: 2018 IBM.
// SPDX-License-Identifier: Apache-2.0
#include "cpuinfo_ppc.h"
#include "internal/bit_utils.h"
#include "internal/filesystem.h"
#include "internal/stack_line_reader.h"
#include "internal/string_view.h"
#include "internal/unix_features_aggregator.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
DECLARE_SETTER_AND_GETTER(PPCFeatures, ppc32)
DECLARE_SETTER_AND_GETTER(PPCFeatures, ppc64)
DECLARE_SETTER_AND_GETTER(PPCFeatures, ppc601)
DECLARE_SETTER_AND_GETTER(PPCFeatures, altivec)
DECLARE_SETTER_AND_GETTER(PPCFeatures, fpu)
DECLARE_SETTER_AND_GETTER(PPCFeatures, mmu)
DECLARE_SETTER_AND_GETTER(PPCFeatures, mac_4xx)
DECLARE_SETTER_AND_GETTER(PPCFeatures, unifiedcache)
DECLARE_SETTER_AND_GETTER(PPCFeatures, spe)
DECLARE_SETTER_AND_GETTER(PPCFeatures, efpsingle)
DECLARE_SETTER_AND_GETTER(PPCFeatures, efpdouble)
DECLARE_SETTER_AND_GETTER(PPCFeatures, no_tb)
DECLARE_SETTER_AND_GETTER(PPCFeatures, power4)
DECLARE_SETTER_AND_GETTER(PPCFeatures, power5)
DECLARE_SETTER_AND_GETTER(PPCFeatures, power5plus)
DECLARE_SETTER_AND_GETTER(PPCFeatures, cell)
DECLARE_SETTER_AND_GETTER(PPCFeatures, booke)
DECLARE_SETTER_AND_GETTER(PPCFeatures, smt)
DECLARE_SETTER_AND_GETTER(PPCFeatures, icachesnoop)
DECLARE_SETTER_AND_GETTER(PPCFeatures, arch205)
DECLARE_SETTER_AND_GETTER(PPCFeatures, pa6t)
DECLARE_SETTER_AND_GETTER(PPCFeatures, dfp)
DECLARE_SETTER_AND_GETTER(PPCFeatures, power6ext)
DECLARE_SETTER_AND_GETTER(PPCFeatures, arch206)
DECLARE_SETTER_AND_GETTER(PPCFeatures, vsx)
DECLARE_SETTER_AND_GETTER(PPCFeatures, pseries_perfmon_compat)
DECLARE_SETTER_AND_GETTER(PPCFeatures, truele)
DECLARE_SETTER_AND_GETTER(PPCFeatures, ppcle)
DECLARE_SETTER_AND_GETTER(PPCFeatures, arch207)
DECLARE_SETTER_AND_GETTER(PPCFeatures, htm)
DECLARE_SETTER_AND_GETTER(PPCFeatures, dscr)
DECLARE_SETTER_AND_GETTER(PPCFeatures, ebb)
DECLARE_SETTER_AND_GETTER(PPCFeatures, isel)
DECLARE_SETTER_AND_GETTER(PPCFeatures, tar)
DECLARE_SETTER_AND_GETTER(PPCFeatures, vcrypto)
DECLARE_SETTER_AND_GETTER(PPCFeatures, htm_nosc)
DECLARE_SETTER_AND_GETTER(PPCFeatures, arch300)
DECLARE_SETTER_AND_GETTER(PPCFeatures, ieee128)
DECLARE_SETTER_AND_GETTER(PPCFeatures, darn)
DECLARE_SETTER_AND_GETTER(PPCFeatures, scv)
DECLARE_SETTER_AND_GETTER(PPCFeatures, htm_no_suspend)
static const CapabilityConfig kConfigs[] = {
[PPC_32] = {{PPC_FEATURE_32, 0}, "ppc32", &set_ppc32, &get_ppc32},
[PPC_64] = {{PPC_FEATURE_64, 0}, "ppc64", &set_ppc64, &get_ppc64},
[PPC_601_INSTR] = {{PPC_FEATURE_601_INSTR, 0}, "ppc601", &set_ppc601, &get_ppc601},
[PPC_HAS_ALTIVEC] = {{PPC_FEATURE_HAS_ALTIVEC, 0}, "altivec", &set_altivec, &get_altivec},
[PPC_HAS_FPU] = {{PPC_FEATURE_HAS_FPU, 0}, "fpu", &set_fpu, &get_fpu},
[PPC_HAS_MMU] = {{PPC_FEATURE_HAS_MMU, 0}, "mmu", &set_mmu, &get_mmu},
[PPC_HAS_4xxMAC] = {{PPC_FEATURE_HAS_4xxMAC, 0}, "4xxmac", &set_mac_4xx, &get_mac_4xx},
[PPC_UNIFIED_CACHE] = {{PPC_FEATURE_UNIFIED_CACHE, 0}, "ucache", &set_unifiedcache, &get_unifiedcache},
[PPC_HAS_SPE] = {{PPC_FEATURE_HAS_SPE, 0}, "spe", &set_spe, &get_spe},
[PPC_HAS_EFP_SINGLE] = {{PPC_FEATURE_HAS_EFP_SINGLE, 0}, "efpsingle", &set_efpsingle, &get_efpsingle},
[PPC_HAS_EFP_DOUBLE] = {{PPC_FEATURE_HAS_EFP_DOUBLE, 0}, "efpdouble", &set_efpdouble, &get_efpdouble},
[PPC_NO_TB] = {{PPC_FEATURE_NO_TB, 0}, "notb", &set_no_tb, &get_no_tb},
[PPC_POWER4] = {{PPC_FEATURE_POWER4, 0}, "power4", &set_power4, &get_power4},
[PPC_POWER5] = {{PPC_FEATURE_POWER5, 0}, "power5", &set_power5, &get_power5},
[PPC_POWER5_PLUS] = {{PPC_FEATURE_POWER5_PLUS, 0}, "power5+", &set_power5plus, &get_power5plus},
[PPC_CELL] = {{PPC_FEATURE_CELL, 0}, "cellbe", &set_cell, &get_cell},
[PPC_BOOKE] = {{PPC_FEATURE_BOOKE, 0}, "booke", &set_booke, &get_booke},
[PPC_SMT] = {{PPC_FEATURE_SMT, 0}, "smt", &set_smt, &get_smt},
[PPC_ICACHE_SNOOP] = {{PPC_FEATURE_ICACHE_SNOOP, 0}, "ic_snoop", &set_icachesnoop, &get_icachesnoop},
[PPC_ARCH_2_05] = {{PPC_FEATURE_ARCH_2_05, 0}, "arch_2_05", &set_arch205, &get_arch205},
[PPC_PA6T] = {{PPC_FEATURE_PA6T, 0}, "pa6t", &set_pa6t, &get_pa6t},
[PPC_HAS_DFP] = {{PPC_FEATURE_HAS_DFP, 0}, "dfp", &set_dfp, &get_dfp},
[PPC_POWER6_EXT] = {{PPC_FEATURE_POWER6_EXT, 0}, "power6x", &set_power6ext, &get_power6ext},
[PPC_ARCH_2_06] = {{PPC_FEATURE_ARCH_2_06, 0}, "arch_2_06", &set_arch206, &get_arch206},
[PPC_HAS_VSX] = {{PPC_FEATURE_HAS_VSX, 0}, "vsx", &set_vsx, &get_vsx},
[PPC_PSERIES_PERFMON_COMPAT] = {{PPC_FEATURE_PSERIES_PERFMON_COMPAT, 0}, "archpmu",
&set_pseries_perfmon_compat, &get_pseries_perfmon_compat},
[PPC_TRUE_LE] = {{PPC_FEATURE_TRUE_LE, 0}, "true_le", &set_truele, &get_truele},
[PPC_PPC_LE] = {{PPC_FEATURE_PPC_LE, 0}, "ppcle", &set_ppcle, &get_ppcle},
[PPC_ARCH_2_07] = {{0, PPC_FEATURE2_ARCH_2_07}, "arch_2_07", &set_arch207, &get_arch207},
[PPC_HTM] = {{0, PPC_FEATURE2_HTM}, "htm", &set_htm, &get_htm},
[PPC_DSCR] = {{0, PPC_FEATURE2_DSCR}, "dscr", &set_dscr, &get_dscr},
[PPC_EBB] = {{0, PPC_FEATURE2_EBB}, "ebb", &set_ebb, &get_ebb},
[PPC_ISEL] = {{0, PPC_FEATURE2_ISEL}, "isel", &set_isel, &get_isel},
[PPC_TAR] = {{0, PPC_FEATURE2_TAR}, "tar", &set_tar, &get_tar},
[PPC_VEC_CRYPTO] = {{0, PPC_FEATURE2_VEC_CRYPTO}, "vcrypto", &set_vcrypto, &get_vcrypto},
[PPC_HTM_NOSC] = {{0, PPC_FEATURE2_HTM_NOSC}, "htm-nosc", &set_htm_nosc, &get_htm_nosc},
[PPC_ARCH_3_00] = {{0, PPC_FEATURE2_ARCH_3_00}, "arch_3_00", &set_arch300, &get_arch300},
[PPC_HAS_IEEE128] = {{0, PPC_FEATURE2_HAS_IEEE128}, "ieee128", &set_ieee128, &get_ieee128},
[PPC_DARN] = {{0, PPC_FEATURE2_DARN}, "darn", &set_darn, &get_darn},
[PPC_SCV] = {{0, PPC_FEATURE2_SCV}, "scv", &set_scv, &get_scv},
[PPC_HTM_NO_SUSPEND] = {{0, PPC_FEATURE2_HTM_NO_SUSPEND}, "htm-no-suspend", &set_htm_no_suspend,
&get_htm_no_suspend},
};
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
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;
CpuFeatures_OverrideFromHwCaps(kConfigsSize, kConfigs,
CpuFeatures_GetHardwareCapabilities(),
&info.features);
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 >= kConfigsSize)
return false;
return kConfigs[value].get_bit((PPCFeatures*)features);
}
const char* GetPPCFeaturesEnumName(PPCFeaturesEnum value)
{
if (value >= kConfigsSize)
return "unknown feature";
return kConfigs[value].proc_cpuinfo_flag;
}

View File

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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

View File

@ -0,0 +1,169 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "internal/hwcaps.h"
#include "cpu_features_macros.h"
#include "internal/filesystem.h"
#include "internal/string_view.h"
#include <stdlib.h>
#include <string.h>
#if defined(NDEBUG)
#define D(...)
#else
#include <stdio.h>
#define D(...) \
do \
{ \
printf(__VA_ARGS__); \
fflush(stdout); \
} \
while (0)
#endif
////////////////////////////////////////////////////////////////////////////////
// Implementation of GetElfHwcapFromGetauxval
////////////////////////////////////////////////////////////////////////////////
#if defined(CPU_FEATURES_MOCK_GET_ELF_HWCAP_FROM_GETAUXVAL)
// Implementation will be provided by test/hwcaps_for_testing.cc.
#elif 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>
#define AT_HWCAP 16
#define AT_HWCAP2 26
#define AT_PLATFORM 15
#define AT_BASE_PLATFORM 24
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;
}

View File

@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "internal/stack_line_reader.h"
#include "internal/filesystem.h"
#include <assert.h>
#include <errno.h>
#include <stdio.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);
}
}
}

View File

@ -0,0 +1,202 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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;
}

View File

@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "internal/unix_features_aggregator.h"
#include "internal/string_view.h"
void CpuFeatures_SetFromFlags(const size_t configs_size,
const CapabilityConfig* configs,
const StringView flags_line,
void* const features)
{
size_t i = 0;
for (; i < configs_size; ++i)
{
const CapabilityConfig config = configs[i];
config.set_bit(features, CpuFeatures_StringView_HasWord(
flags_line, config.proc_cpuinfo_flag));
}
}
static bool IsSet(const uint32_t mask, const uint32_t value)
{
if (mask == 0) return false;
return (value & mask) == mask;
}
static bool IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
const HardwareCapabilities hwcaps)
{
return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) ||
IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
}
void CpuFeatures_OverrideFromHwCaps(const size_t configs_size,
const CapabilityConfig* configs,
const HardwareCapabilities hwcaps,
void* const features)
{
size_t i = 0;
for (; i < configs_size; ++i)
{
const CapabilityConfig* config = &configs[i];
if (IsHwCapsSet(config->hwcaps_mask, hwcaps))
{
config->set_bit(features, true);
}
}
}

View File

@ -0,0 +1,469 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// This program dumps current host data to the standard output.
// Output can be text or json if the `--json` flag is passed.
#include "cpu_features_macros.h"
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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 intented 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;
}

View File

@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: 2017 Google Inc.
# SPDX-License-Identifier: Apache-2.0
#
# 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_compile_definitions(hwcaps_for_testing PUBLIC CPU_FEATURES_MOCK_GET_ELF_HWCAP_FROM_GETAUXVAL)
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/stack_line_reader.c ../src/unix_features_aggregator.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)
##------------------------------------------------------------------------------
## unix_features_aggregator_test
add_executable(unix_features_aggregator_test unix_features_aggregator_test.cc)
target_link_libraries(unix_features_aggregator_test all_libraries)
add_test(NAME unix_features_aggregator_test COMMAND unix_features_aggregator_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)
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()

View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "gtest/gtest.h"
#include "internal/bit_utils.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

View File

@ -0,0 +1,116 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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, 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);
}
} // namespace
} // namespace cpu_features

View File

@ -0,0 +1,352 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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

View File

@ -0,0 +1,119 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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

View File

@ -0,0 +1,281 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "cpuinfo_x86.h"
#include "gtest/gtest.h"
#include "internal/cpuid_x86.h"
#include <cassert>
#include <cstdio>
#include <map>
namespace cpu_features
{
class FakeCpu
{
public:
Leaf CpuIdEx(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;
}
private:
std::map<std::pair<uint32_t, int>, Leaf> cpuid_leaves_;
uint32_t xcr0_eax_;
};
auto* g_fake_cpu = new FakeCpu();
extern "C" Leaf CpuIdEx(uint32_t leaf_id, int ecx)
{
return g_fake_cpu->CpuIdEx(leaf_id, ecx);
}
extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
namespace
{
TEST(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_4vbmi2);
// 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(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(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(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(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(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(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 ");
}
// 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

View File

@ -0,0 +1,104 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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

View File

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
// Implements a fake filesystem, useful for tests.
#ifndef CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
#define CPU_FEATURES_TEST_FILESYSTEM_FOR_TESTING_H_
#include "internal/filesystem.h"
#include <memory>
#include <string>
#include <unordered_map>
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_

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "hwcaps_for_testing.h"
#include "internal/string_view.h"
#include <string.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

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#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_

View File

@ -0,0 +1,125 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "filesystem_for_testing.h"
#include "gtest/gtest.h"
#include "internal/stack_line_reader.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

View File

@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "gtest/gtest.h"
#include "internal/string_view.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);
// 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);
// 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_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("a"), str("a")));
EXPECT_FALSE(CpuFeatures_StringView_IsEquals(str("a"), str("b")));
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_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("-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

View File

@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2017 Google Inc.
// SPDX-License-Identifier: Apache-2.0
#include "gtest/gtest.h"
#include "internal/unix_features_aggregator.h"
#include <array>
namespace cpu_features
{
namespace
{
struct Features
{
bool a = false;
bool b = false;
bool c = false;
};
enum eFeatures
{
TEST_a,
TEST_b,
TEST_c
};
DECLARE_SETTER_AND_GETTER(Features, a)
DECLARE_SETTER_AND_GETTER(Features, b)
DECLARE_SETTER_AND_GETTER(Features, c)
class LinuxFeatureAggregatorTest : public testing::Test
{
public:
const std::array<CapabilityConfig, 3> kConfigs = {{{{0b0001, 0b0000}, "a", &set_a, &get_a},
{{0b0010, 0b0000}, "b", &set_b, &get_b},
{{0b0000, 0b1100}, "c", &set_c, &get_c}}};
};
TEST_F(LinuxFeatureAggregatorTest, FromFlagsEmpty)
{
Features features;
CpuFeatures_SetFromFlags(kConfigs.size(), kConfigs.data(), str(""),
&features);
EXPECT_FALSE(features.a);
EXPECT_FALSE(features.b);
EXPECT_FALSE(features.c);
EXPECT_FALSE(kConfigs[TEST_a].get_bit(&features));
}
TEST_F(LinuxFeatureAggregatorTest, FromFlagsAllSet)
{
Features features;
CpuFeatures_SetFromFlags(kConfigs.size(), kConfigs.data(), str("a c b"),
&features);
EXPECT_TRUE(features.a);
EXPECT_TRUE(features.b);
EXPECT_TRUE(features.c);
EXPECT_TRUE(kConfigs[TEST_a].get_bit(&features));
}
TEST_F(LinuxFeatureAggregatorTest, FromFlagsOnlyA)
{
Features features;
CpuFeatures_SetFromFlags(kConfigs.size(), kConfigs.data(), str("a"),
&features);
EXPECT_TRUE(features.a);
EXPECT_FALSE(features.b);
EXPECT_FALSE(features.c);
EXPECT_TRUE(kConfigs[TEST_a].get_bit(&features));
EXPECT_FALSE(kConfigs[TEST_b].get_bit(&features));
EXPECT_FALSE(kConfigs[TEST_c].get_bit(&features));
}
TEST_F(LinuxFeatureAggregatorTest, FromHwcapsNone)
{
HardwareCapabilities capability;
capability.hwcaps = 0; // matches none
capability.hwcaps2 = 0; // matches none
Features features;
CpuFeatures_OverrideFromHwCaps(kConfigs.size(), kConfigs.data(), capability,
&features);
EXPECT_FALSE(features.a);
EXPECT_FALSE(features.b);
EXPECT_FALSE(features.c);
}
TEST_F(LinuxFeatureAggregatorTest, FromHwcapsSet)
{
HardwareCapabilities capability;
capability.hwcaps = 0b0010; // matches b but not a
capability.hwcaps2 = 0b1111; // matches c
Features features;
CpuFeatures_OverrideFromHwCaps(kConfigs.size(), kConfigs.data(), capability,
&features);
EXPECT_FALSE(features.a);
EXPECT_TRUE(features.b);
EXPECT_TRUE(features.c);
}
} // namespace
} // namespace cpu_features

View File

@ -20,7 +20,7 @@
<flag compiler="gnu">-funsafe-math-optimizations</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neon"></check>
<check name="neon"></check>
</arch>
<arch name="neonv7">
@ -29,14 +29,14 @@
<flag compiler="clang">-mfpu=neon</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neonv7"></check>
<check name="neon"></check>
</arch>
<arch name="neonv8">
<flag compiler="gnu">-funsafe-math-optimizations</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neonv8"></check>
<check name="neon"></check>
</arch>
<arch name="32">
@ -45,42 +45,20 @@
</arch>
<arch name="64">
<check name="check_extended_cpuid">
<param>0x80000001</param>
</check>
<check name="cpuid_x86_bit"> <!-- checks to see if a bit is set -->
<param>3</param> <!-- eax, ebx, ecx, [edx] -->
<param>0x80000001</param> <!-- cpuid operation -->
<param>29</param> <!-- bit shift -->
</check>
<!-- <check name="check_extended_cpuid"> -->
<!-- <param>0x80000001</param>
</check> -->
<!--<check name="cpuid_x86_bit"> checks to see if a bit is set -->
<!-- <param>3</param> eax, ebx, ecx, [edx] -->
<!-- <param>0x80000001</param> cpuid operation -->
<!-- <param>29</param> bit shift -->
<!-- </check> -->
<flag compiler="gnu">-m64</flag>
<flag compiler="clang">-m64</flag>
</arch>
<arch name="3dnow">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x80000001</param>
<param>31</param>
</check>
<flag compiler="gnu">-m3dnow</flag>
<flag compiler="clang">-m3dnow</flag>
<alignment>8</alignment>
</arch>
<arch name="abm">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x80000001</param>
<param>5</param>
</check>
<flag compiler="gnu">-msse4.2</flag>
<flag compiler="clang">-msse4.2</flag>
<alignment>16</alignment>
</arch>
<arch name="popcount">
<check name="cpuid_x86_bit">
<check name="popcnt">
<param>2</param>
<param>0x00000001</param>
<param>23</param>
@ -91,7 +69,7 @@
</arch>
<arch name="mmx">
<check name="cpuid_x86_bit">
<check name="mmx">
<param>3</param>
<param>0x00000001</param>
<param>23</param>
@ -103,7 +81,7 @@
</arch>
<arch name="fma">
<check name="cpuid_x86_bit">
<check name="fma3">
<param>2</param>
<param>0x00000001</param>
<param>12</param>
@ -115,7 +93,7 @@
</arch>
<arch name="sse">
<check name="cpuid_x86_bit">
<check name="sse">
<param>3</param>
<param>0x00000001</param>
<param>25</param>
@ -129,7 +107,7 @@
</arch>
<arch name="sse2">
<check name="cpuid_x86_bit">
<check name="sse2">
<param>3</param>
<param>0x00000001</param>
<param>26</param>
@ -148,7 +126,7 @@
</arch>
<arch name="sse3">
<check name="cpuid_x86_bit">
<check name="sse3">
<param>2</param>
<param>0x00000001</param>
<param>0</param>
@ -162,7 +140,7 @@
</arch>
<arch name="ssse3">
<check name="cpuid_x86_bit">
<check name="ssse3">
<param>2</param>
<param>0x00000001</param>
<param>9</param>
@ -174,7 +152,7 @@
</arch>
<arch name="sse4_a">
<check name="cpuid_x86_bit">
<check name="sse4a">
<param>2</param>
<param>0x80000001</param>
<param>6</param>
@ -185,7 +163,7 @@
</arch>
<arch name="sse4_1">
<check name="cpuid_x86_bit">
<check name="sse4_1">
<param>2</param>
<param>0x00000001</param>
<param>19</param>
@ -197,7 +175,7 @@
</arch>
<arch name="sse4_2">
<check name="cpuid_x86_bit">
<check name="sse4_2">
<param>2</param>
<param>0x00000001</param>
<param>20</param>
@ -209,19 +187,11 @@
</arch>
<arch name="avx">
<check name="cpuid_x86_bit">
<check name="avx">
<param>2</param>
<param>0x00000001</param>
<param>28</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX -->
<check name="get_avx_enabled"></check>
<flag compiler="gnu">-mavx</flag>
<flag compiler="clang">-mavx</flag>
<flag compiler="msvc">/arch:AVX</flag>
@ -229,20 +199,12 @@
</arch>
<arch name="avx2">
<check name="cpuid_count_x86_bit">
<check name="avx2">
<param>7</param>
<param>0</param>
<param>1</param>
<param>5</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX2 -->
<check name="get_avx2_enabled"></check>
<flag compiler="gnu">-mavx2</flag>
<flag compiler="clang">-mavx2</flag>
<flag compiler="msvc">/arch:AVX2</flag>
@ -251,20 +213,12 @@
<arch name="avx512f">
<!-- check for AVX512F -->
<check name="cpuid_count_x86_bit">
<check name="avx512f">
<param>7</param>
<param>0</param>
<param>1</param>
<param>16</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX512 -->
<check name="get_avx512_enabled"></check>
<flag compiler="gnu">-mavx512f</flag>
<flag compiler="clang">-mavx512f</flag>
<flag compiler="msvc">/arch:AVX512F</flag>
@ -273,20 +227,12 @@
<arch name="avx512cd">
<!-- check for AVX512CD -->
<check name="cpuid_count_x86_bit">
<check name="avx512cd">
<param>7</param>
<param>0</param>
<param>1</param>
<param>28</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX512 -->
<check name="get_avx512_enabled"></check>
<flag compiler="gnu">-mavx512cd</flag>
<flag compiler="clang">-mavx512cd</flag>
<flag compiler="msvc">/arch:AVX512CD</flag>

View File

@ -0,0 +1,296 @@
<!-- archs appear in order of significance for blind, de-facto version ordering -->
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
<!-- SPDX-FileCopyrightText: 2014-2020 Carles Fernandez-Prades <carles.fernandez@cttc.es> -->
<grammar>
<arch name="generic"> <!-- name is required-->
</arch>
<arch name="softfp">
<flag compiler="gnu">-mfloat-abi=softfp</flag>
<flag compiler="clang">-mfloat-abi=softfp</flag>
</arch>
<arch name="hardfp">
<flag compiler="gnu">-mfloat-abi=hard</flag>
<flag compiler="clang">-mfloat-abi=hard</flag>
</arch>
<arch name="neon">
<flag compiler="gnu">-funsafe-math-optimizations</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neon"></check>
</arch>
<arch name="neonv7">
<flag compiler="gnu">-mfpu=neon</flag>
<flag compiler="gnu">-funsafe-math-optimizations</flag>
<flag compiler="clang">-mfpu=neon</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neonv7"></check>
</arch>
<arch name="neonv8">
<flag compiler="gnu">-funsafe-math-optimizations</flag>
<flag compiler="clang">-funsafe-math-optimizations</flag>
<alignment>16</alignment>
<check name="has_neonv8"></check>
</arch>
<arch name="32">
<flag compiler="gnu">-m32</flag>
<flag compiler="clang">-m32</flag>
</arch>
<arch name="64">
<check name="check_extended_cpuid">
<param>0x80000001</param>
</check>
<check name="cpuid_x86_bit"> <!-- checks to see if a bit is set -->
<param>3</param> <!-- eax, ebx, ecx, [edx] -->
<param>0x80000001</param> <!-- cpuid operation -->
<param>29</param> <!-- bit shift -->
</check>
<flag compiler="gnu">-m64</flag>
<flag compiler="clang">-m64</flag>
</arch>
<arch name="3dnow">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x80000001</param>
<param>31</param>
</check>
<flag compiler="gnu">-m3dnow</flag>
<flag compiler="clang">-m3dnow</flag>
<alignment>8</alignment>
</arch>
<arch name="abm">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x80000001</param>
<param>5</param>
</check>
<flag compiler="gnu">-msse4.2</flag>
<flag compiler="clang">-msse4.2</flag>
<alignment>16</alignment>
</arch>
<arch name="popcount">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>23</param>
</check>
<flag compiler="gnu">-mpopcnt</flag>
<flag compiler="clang">-mpopcnt</flag>
<flag compiler="msvc">/arch:AVX</flag>
</arch>
<arch name="mmx">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x00000001</param>
<param>23</param>
</check>
<flag compiler="gnu">-mmmx</flag>
<flag compiler="clang">-mmmx</flag>
<flag compiler="msvc">/arch:SSE</flag>
<alignment>8</alignment>
</arch>
<arch name="fma">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>12</param>
</check>
<flag compiler="gnu">-mfma</flag>
<flag compiler="clang">-mfma</flag>
<flag compiler="msvc">/arch:AVX2</flag>
<alignment>32</alignment>
</arch>
<arch name="sse">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x00000001</param>
<param>25</param>
</check>
<flag compiler="gnu">-msse</flag>
<flag compiler="clang">-msse</flag>
<flag compiler="msvc">/arch:SSE</flag>
<environment>_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);</environment>
<include>xmmintrin.h</include>
<alignment>16</alignment>
</arch>
<arch name="sse2">
<check name="cpuid_x86_bit">
<param>3</param>
<param>0x00000001</param>
<param>26</param>
</check>
<flag compiler="gnu">-msse2</flag>
<flag compiler="clang">-msse2</flag>
<flag compiler="msvc">/arch:SSE2</flag>
<alignment>16</alignment>
</arch>
<arch name="orc">
</arch>
<!-- it's here for overrule stuff. -->
<arch name="norc">
</arch>
<arch name="sse3">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>0</param>
</check>
<flag compiler="gnu">-msse3</flag>
<flag compiler="clang">-msse3</flag>
<flag compiler="msvc">/arch:AVX</flag>
<environment>_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);</environment>
<include>pmmintrin.h</include>
<alignment>16</alignment>
</arch>
<arch name="ssse3">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>9</param>
</check>
<flag compiler="gnu">-mssse3</flag>
<flag compiler="clang">-mssse3</flag>
<flag compiler="msvc">/arch:AVX</flag>
<alignment>16</alignment>
</arch>
<arch name="sse4_a">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x80000001</param>
<param>6</param>
</check>
<flag compiler="gnu">-msse4a</flag>
<flag compiler="clang">-msse4a</flag>
<alignment>16</alignment>
</arch>
<arch name="sse4_1">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>19</param>
</check>
<flag compiler="gnu">-msse4.1</flag>
<flag compiler="clang">-msse4.1</flag>
<flag compiler="msvc">/arch:AVX</flag>
<alignment>16</alignment>
</arch>
<arch name="sse4_2">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>20</param>
</check>
<flag compiler="gnu">-msse4.2</flag>
<flag compiler="clang">-msse4.2</flag>
<flag compiler="msvc">/arch:AVX</flag>
<alignment>16</alignment>
</arch>
<arch name="avx">
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>28</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX -->
<check name="get_avx_enabled"></check>
<flag compiler="gnu">-mavx</flag>
<flag compiler="clang">-mavx</flag>
<flag compiler="msvc">/arch:AVX</flag>
<alignment>32</alignment>
</arch>
<arch name="avx2">
<check name="cpuid_count_x86_bit">
<param>7</param>
<param>0</param>
<param>1</param>
<param>5</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX2 -->
<check name="get_avx2_enabled"></check>
<flag compiler="gnu">-mavx2</flag>
<flag compiler="clang">-mavx2</flag>
<flag compiler="msvc">/arch:AVX2</flag>
<alignment>32</alignment>
</arch>
<arch name="avx512f">
<!-- check for AVX512F -->
<check name="cpuid_count_x86_bit">
<param>7</param>
<param>0</param>
<param>1</param>
<param>16</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX512 -->
<check name="get_avx512_enabled"></check>
<flag compiler="gnu">-mavx512f</flag>
<flag compiler="clang">-mavx512f</flag>
<flag compiler="msvc">/arch:AVX512F</flag>
<alignment>64</alignment>
</arch>
<arch name="avx512cd">
<!-- check for AVX512CD -->
<check name="cpuid_count_x86_bit">
<param>7</param>
<param>0</param>
<param>1</param>
<param>28</param>
</check>
<!-- check to make sure that xgetbv is enabled in OS -->
<check name="cpuid_x86_bit">
<param>2</param>
<param>0x00000001</param>
<param>27</param>
</check>
<!-- check to see that the OS has enabled AVX512 -->
<check name="get_avx512_enabled"></check>
<flag compiler="gnu">-mavx512cd</flag>
<flag compiler="clang">-mavx512cd</flag>
<flag compiler="msvc">/arch:AVX512CD</flag>
<alignment>64</alignment>
</arch>
</grammar>

View File

@ -53,7 +53,7 @@ def register_arch(**kwargs):
from xml.dom import minidom
import os
gendir = os.path.dirname(__file__)
archs_xml = minidom.parse(os.path.join(gendir, 'archs.xml')).getElementsByTagName('arch')
archs_xml = minidom.parse(os.path.join(gendir, '@VOLK_GNSSSSDR_ARCHS_XML_FILE@')).getElementsByTagName('arch')
for arch_xml in archs_xml:
kwargs = dict()
for attr in arch_xml.attributes.keys():

View File

@ -82,6 +82,16 @@ endif()
if(NOT PYTHON_DASH_B)
set(PYTHON_DASH_B "")
endif()
if(USE_CPU_FEATURES)
set(VOLK_GNSSSSDR_ARCHS_XML_FILE "archs.xml")
else()
set(VOLK_GNSSSSDR_ARCHS_XML_FILE "archs_old.xml")
endif()
configure_file(${PROJECT_SOURCE_DIR}/gen/volk_gnsssdr_arch_defs.py.in
${PROJECT_SOURCE_DIR}/gen/volk_gnsssdr_arch_defs.py
@ONLY
)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B}
${PROJECT_SOURCE_DIR}/gen/volk_gnsssdr_compile_utils.py
@ -338,6 +348,11 @@ message(STATUS "Available machines: ${available_machines}")
# dependencies are all python, xml, and header implementation files
file(GLOB xml_files ${PROJECT_SOURCE_DIR}/gen/*.xml)
if(USE_CPU_FEATURES)
list(REMOVE_ITEM xml_files ${PROJECT_SOURCE_DIR}/gen/archs_old.xml)
else()
list(REMOVE_ITEM xml_files ${PROJECT_SOURCE_DIR}/gen/archs.xml)
endif()
list(SORT xml_files)
file(GLOB py_files ${PROJECT_SOURCE_DIR}/gen/*.py)
list(SORT py_files)
@ -361,7 +376,11 @@ gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr.tmpl.h ${PROJE
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr.tmpl.c ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr.c)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_typedefs.tmpl.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_typedefs.h)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_cpu.tmpl.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_cpu.h)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_cpu.tmpl.c ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr_cpu.c)
if(USE_CPU_FEATURES)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_cpu.tmpl.c ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr_cpu.c)
else()
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_cpu.tmpl.old.c ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr_cpu.c)
endif()
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_config_fixed.tmpl.h ${PROJECT_BINARY_DIR}/include/volk_gnsssdr/volk_gnsssdr_config_fixed.h)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_machines.tmpl.h ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr_machines.h)
gen_template(${PROJECT_SOURCE_DIR}/tmpl/volk_gnsssdr_machines.tmpl.c ${PROJECT_BINARY_DIR}/lib/volk_gnsssdr_machines.c)
@ -553,6 +572,12 @@ if(NOT (CMAKE_GENERATOR STREQUAL Xcode))
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)
if(USE_CPU_FEATURES)
target_include_directories(volk_gnsssdr_obj
PRIVATE
$<TARGET_PROPERTY:cpu_features,INTERFACE_INCLUDE_DIRECTORIES>
)
endif()
# Configure object target properties
if(NOT MSVC)
set_target_properties(volk_gnsssdr_obj PROPERTIES COMPILE_FLAGS "-fPIC")
@ -565,7 +590,13 @@ if(CMAKE_GENERATOR STREQUAL Xcode)
else()
add_library(volk_gnsssdr SHARED $<TARGET_OBJECTS:volk_gnsssdr_obj>)
endif()
target_link_libraries(volk_gnsssdr PUBLIC ${volk_gnsssdr_libraries})
if(USE_CPU_FEATURES)
target_link_libraries(volk_gnsssdr
PRIVATE ${volk_gnsssdr_libraries} cpu_features
)
else()
target_link_libraries(volk_gnsssdr PUBLIC ${volk_gnsssdr_libraries})
endif()
target_include_directories(volk_gnsssdr
PUBLIC $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -574,7 +605,12 @@ target_include_directories(volk_gnsssdr
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC $<INSTALL_INTERFACE:include>
)
if(USE_CPU_FEATURES)
target_include_directories(volk_gnsssdr
PRIVATE
$<TARGET_PROPERTY:cpu_features,INTERFACE_INCLUDE_DIRECTORIES>
)
endif()
# Configure target properties
if(NOT MSVC)
target_link_libraries(volk_gnsssdr PUBLIC m)
@ -597,6 +633,11 @@ if(ENABLE_STATIC_LIBS)
else()
add_library(volk_gnsssdr_static STATIC $<TARGET_OBJECTS:volk_gnsssdr_obj>)
endif()
if(USE_CPU_FEATURES)
target_link_libraries(volk_gnsssdr_static
PRIVATE cpu_features
)
endif()
target_link_libraries(volk_gnsssdr_static PUBLIC ${volk_gnsssdr_libraries} pthread)
if(NOT MSVC)
target_link_libraries(volk_gnsssdr_static PUBLIC m)
@ -609,6 +650,12 @@ if(ENABLE_STATIC_LIBS)
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
)
if(USE_CPU_FEATURES)
target_include_directories(volk_gnsssdr_static
PRIVATE
$<TARGET_PROPERTY:cpu_features,INTERFACE_INCLUDE_DIRECTORIES>
)
endif()
set_target_properties(volk_gnsssdr_static PROPERTIES OUTPUT_NAME volk_gnsssdr)

View File

@ -14,204 +14,40 @@
#include <string.h>
// clang-format on
#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
// #if defined(__cplusplus)
// using namespace cpu_features;
//#endif
struct VOLK_CPU volk_gnsssdr_cpu;
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
#define VOLK_CPU_x86
#endif
#if defined(VOLK_CPU_x86)
//implement get cpuid for gcc compilers using a system or local copy of cpuid.h
#if defined(__GNUC__)
#include <cpuid.h>
#define cpuid_x86(op, r) __get_cpuid(op, (unsigned int *)r + 0, (unsigned int *)r + 1, (unsigned int *)r + 2, (unsigned int *)r + 3)
#define cpuid_x86_count(op, count, regs) __cpuid_count(op, count, *((unsigned int *)regs), *((unsigned int *)regs + 1), *((unsigned int *)regs + 2), *((unsigned int *)regs + 3))
/* Return Intel AVX extended CPU capabilities register.
* This function will bomb on non-AVX-capable machines, so
* check for AVX capability before executing.
*/
#if ((__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2) || (__clang_major__ >= 3)) && defined(HAVE_XGETBV)
static inline unsigned long long _xgetbv(unsigned int index)
{
unsigned int eax, edx;
__VOLK_ASM __VOLK_VOLATILE("xgetbv"
: "=a"(eax), "=d"(edx)
: "c"(index));
return ((unsigned long long)edx << 32) | eax;
}
#define __xgetbv() _xgetbv(0)
#else
#define __xgetbv() 0
#endif
//implement get cpuid for MSVC compilers using __cpuid intrinsic
#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H)
#include <intrin.h>
#define cpuid_x86(op, r) __cpuid(((int *)r), op)
#define cpuid_x86_count(op, count, regs) __cpuidex((int *)regs, op, count)
#if defined(_XCR_XFEATURE_ENABLED_MASK)
#define __xgetbv() _xgetbv(_XCR_XFEATURE_ENABLED_MASK)
#else
#define __xgetbv() 0
#endif
#else
#error "A get cpuid for volk_gnsssdr is not available on this compiler..."
#endif //defined(__GNUC__)
#endif //defined(VOLK_CPU_x86)
static inline unsigned int cpuid_count_x86_bit(unsigned int level, unsigned int count, unsigned int reg, unsigned int bit)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4] = {0};
cpuid_x86_count(level, count, regs);
return regs[reg] >> bit & 0x01;
#else
return 0;
#endif
}
static inline unsigned int cpuid_x86_bit(unsigned int reg, unsigned int op, unsigned int bit)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4];
memset(regs, 0, sizeof(unsigned int) * 4);
cpuid_x86(op, regs);
return regs[reg] >> bit & 0x01;
#else
return 0;
#endif
}
static inline unsigned int check_extended_cpuid(unsigned int val)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4];
memset(regs, 0, sizeof(unsigned int) * 4);
cpuid_x86(0x80000000, regs);
return regs[0] >= val;
#else
return 0;
#endif
}
static inline unsigned int get_avx_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0x6;
#else
return 0;
#endif
}
static inline unsigned int get_avx2_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0x6;
#else
return 0;
#endif
}
static inline unsigned int get_avx512_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0xE6; //check for zmm, xmm and ymm regs
#else
return 0;
#endif
}
//neon detection is linux specific
#if defined(__arm__) && defined(__linux__)
#include <asm/hwcap.h>
#include <linux/auxvec.h>
#include <stdio.h>
#define VOLK_CPU_ARMV7
#endif
static int has_neonv7(void)
{
#if defined(VOLK_CPU_ARMV7)
FILE *auxvec_f;
unsigned long auxvec[2];
unsigned int found_neon = 0;
auxvec_f = fopen("/proc/self/auxv", "rb");
if (!auxvec_f) return 0;
size_t r = 1;
//so auxv is basically 32b of ID and 32b of value
//so it goes like this
while (!found_neon && r)
{
r = fread(auxvec, sizeof(unsigned long), 2, auxvec_f);
if ((auxvec[0] == AT_HWCAP) && (auxvec[1] & HWCAP_NEON))
found_neon = 1;
}
fclose(auxvec_f);
return found_neon;
#else
return 0;
#endif
}
//\todo: Fix this to really check for neon on aarch64
//neon detection is linux specific
#if defined(__aarch64__) && defined(__linux__)
#include <asm/hwcap.h>
#include <linux/auxvec.h>
#include <stdio.h>
#define VOLK_CPU_ARMV8
#endif
static int has_neonv8(void)
{
#if defined(VOLK_CPU_ARMV8)
FILE *auxvec_f;
unsigned long auxvec[2];
unsigned int found_neon = 0;
auxvec_f = fopen("/proc/self/auxv", "rb");
if (!auxvec_f) return 0;
size_t r = 1;
//so auxv is basically 32b of ID and 32b of value
//so it goes like this
while (!found_neon && r)
{
r = fread(auxvec, sizeof(unsigned long), 2, auxvec_f);
if ((auxvec[0] == AT_HWCAP) && (auxvec[1] & HWCAP_ASIMD))
found_neon = 1;
}
fclose(auxvec_f);
return found_neon;
#else
return 0;
#endif
}
static int has_neon(void)
{
#if defined(VOLK_CPU_ARMV8) || defined(VOLK_CPU_ARMV7)
if (has_neonv7() || has_neonv8())
return 1;
else
return 0;
#else
return 0;
#endif
}
// clang-format off
%for arch in archs:
static int i_can_has_${arch.name} (void) {
%for check, params in arch.checks:
if (${check}(<% joined_params = ', '.join(params)%>${joined_params}) == 0) return 0;
%if "neon" in arch.name:
#if defined(CPU_FEATURES_ARCH_ARM)
if (GetArmInfo().features.${check} == 0){ return 0; }
#endif
%else:
#if defined(CPU_FEATURES_ARCH_X86)
if (GetX86Info().features.${check} == 0){ return 0; }
#endif
%endif
%endfor
return 1;
}

View File

@ -0,0 +1,259 @@
/* Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors)
*
* GNSS-SDR is a software-defined Global Navigation Satellite Systems receiver
*
* This file is part of GNSS-SDR.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
// clang-format off
#include <volk_gnsssdr/volk_gnsssdr_cpu.h>
#include <volk_gnsssdr/volk_gnsssdr_config_fixed.h>
#include <stdlib.h>
#include <string.h>
// clang-format on
struct VOLK_CPU volk_gnsssdr_cpu;
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
#define VOLK_CPU_x86
#endif
#if defined(VOLK_CPU_x86)
//implement get cpuid for gcc compilers using a system or local copy of cpuid.h
#if defined(__GNUC__)
#include <cpuid.h>
#define cpuid_x86(op, r) __get_cpuid(op, (unsigned int *)r + 0, (unsigned int *)r + 1, (unsigned int *)r + 2, (unsigned int *)r + 3)
#define cpuid_x86_count(op, count, regs) __cpuid_count(op, count, *((unsigned int *)regs), *((unsigned int *)regs + 1), *((unsigned int *)regs + 2), *((unsigned int *)regs + 3))
/* Return Intel AVX extended CPU capabilities register.
* This function will bomb on non-AVX-capable machines, so
* check for AVX capability before executing.
*/
#if ((__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2) || (__clang_major__ >= 3)) && defined(HAVE_XGETBV)
static inline unsigned long long _xgetbv(unsigned int index)
{
unsigned int eax, edx;
__VOLK_ASM __VOLK_VOLATILE("xgetbv"
: "=a"(eax), "=d"(edx)
: "c"(index));
return ((unsigned long long)edx << 32) | eax;
}
#define __xgetbv() _xgetbv(0)
#else
#define __xgetbv() 0
#endif
//implement get cpuid for MSVC compilers using __cpuid intrinsic
#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H)
#include <intrin.h>
#define cpuid_x86(op, r) __cpuid(((int *)r), op)
#define cpuid_x86_count(op, count, regs) __cpuidex((int *)regs, op, count)
#if defined(_XCR_XFEATURE_ENABLED_MASK)
#define __xgetbv() _xgetbv(_XCR_XFEATURE_ENABLED_MASK)
#else
#define __xgetbv() 0
#endif
#else
#error "A get cpuid for volk_gnsssdr is not available on this compiler..."
#endif //defined(__GNUC__)
#endif //defined(VOLK_CPU_x86)
static inline unsigned int cpuid_count_x86_bit(unsigned int level, unsigned int count, unsigned int reg, unsigned int bit)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4] = {0};
cpuid_x86_count(level, count, regs);
return regs[reg] >> bit & 0x01;
#else
return 0;
#endif
}
static inline unsigned int cpuid_x86_bit(unsigned int reg, unsigned int op, unsigned int bit)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4];
memset(regs, 0, sizeof(unsigned int) * 4);
cpuid_x86(op, regs);
return regs[reg] >> bit & 0x01;
#else
return 0;
#endif
}
static inline unsigned int check_extended_cpuid(unsigned int val)
{
#if defined(VOLK_CPU_x86)
unsigned int regs[4];
memset(regs, 0, sizeof(unsigned int) * 4);
cpuid_x86(0x80000000, regs);
return regs[0] >= val;
#else
return 0;
#endif
}
static inline unsigned int get_avx_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0x6;
#else
return 0;
#endif
}
static inline unsigned int get_avx2_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0x6;
#else
return 0;
#endif
}
static inline unsigned int get_avx512_enabled(void)
{
#if defined(VOLK_CPU_x86)
return __xgetbv() & 0xE6; //check for zmm, xmm and ymm regs
#else
return 0;
#endif
}
//neon detection is linux specific
#if defined(__arm__) && defined(__linux__)
#include <asm/hwcap.h>
#include <linux/auxvec.h>
#include <stdio.h>
#define VOLK_CPU_ARMV7
#endif
static int has_neonv7(void)
{
#if defined(VOLK_CPU_ARMV7)
FILE *auxvec_f;
unsigned long auxvec[2];
unsigned int found_neon = 0;
auxvec_f = fopen("/proc/self/auxv", "rb");
if (!auxvec_f) return 0;
size_t r = 1;
//so auxv is basically 32b of ID and 32b of value
//so it goes like this
while (!found_neon && r)
{
r = fread(auxvec, sizeof(unsigned long), 2, auxvec_f);
if ((auxvec[0] == AT_HWCAP) && (auxvec[1] & HWCAP_NEON))
found_neon = 1;
}
fclose(auxvec_f);
return found_neon;
#else
return 0;
#endif
}
//\todo: Fix this to really check for neon on aarch64
//neon detection is linux specific
#if defined(__aarch64__) && defined(__linux__)
#include <asm/hwcap.h>
#include <linux/auxvec.h>
#include <stdio.h>
#define VOLK_CPU_ARMV8
#endif
static int has_neonv8(void)
{
#if defined(VOLK_CPU_ARMV8)
FILE *auxvec_f;
unsigned long auxvec[2];
unsigned int found_neon = 0;
auxvec_f = fopen("/proc/self/auxv", "rb");
if (!auxvec_f) return 0;
size_t r = 1;
//so auxv is basically 32b of ID and 32b of value
//so it goes like this
while (!found_neon && r)
{
r = fread(auxvec, sizeof(unsigned long), 2, auxvec_f);
if ((auxvec[0] == AT_HWCAP) && (auxvec[1] & HWCAP_ASIMD))
found_neon = 1;
}
fclose(auxvec_f);
return found_neon;
#else
return 0;
#endif
}
static int has_neon(void)
{
#if defined(VOLK_CPU_ARMV8) || defined(VOLK_CPU_ARMV7)
if (has_neonv7() || has_neonv8())
return 1;
else
return 0;
#else
return 0;
#endif
}
// clang-format off
%for arch in archs:
static int i_can_has_${arch.name} (void) {
%for check, params in arch.checks:
if (${check}(<% joined_params = ', '.join(params)%>${joined_params}) == 0) return 0;
%endfor
return 1;
}
%endfor
#if defined(HAVE_FENV_H)
#if defined(FE_TONEAREST)
#include <fenv.h>
static inline void set_float_rounding(void){
fesetround(FE_TONEAREST);
}
#else
static inline void set_float_rounding(void){
//do nothing
}
#endif
#elif defined(_MSC_VER)
#include <float.h>
static inline void set_float_rounding(void){
unsigned int cwrd;
_controlfp_s(&cwrd, 0, 0);
_controlfp_s(&cwrd, _RC_NEAR, _MCW_RC);
}
#else
static inline void set_float_rounding(void){
//do nothing
}
#endif
void volk_gnsssdr_cpu_init() {
%for arch in archs:
volk_gnsssdr_cpu.has_${arch.name} = &i_can_has_${arch.name};
%endfor
set_float_rounding();
}
unsigned int volk_gnsssdr_get_lvarch() {
unsigned int retval = 0;
volk_gnsssdr_cpu_init();
%for arch in archs:
retval += volk_gnsssdr_cpu.has_${arch.name}() << LV_${arch.name.upper()};
%endfor
return retval;
}
// clang-format on