Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/cross-sve.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ jobs:
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/.github/toolchains/gcc-aarch64-linux-gnu.cmake
- name: Build
run: cmake --build _build
- name: Set CPU feature test expectations
run: |
echo "XSIMD_TEST_CPU_ASSUME_SSE4_2=0" >> "$GITHUB_ENV"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't that be easy to just not set it instead of setting it to zero?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we have two cases:

  • not defined (the default): Do not run the tests. This is important so that the test runs properly locally without failing.
  • Define to zero: Explicitly check the absence of a feature. This is important so that we do not detect a higher architecture at runtime than is available and crash the program.

echo "XSIMD_TEST_CPU_ASSUME_NEON64=1" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_SVE=1" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_MANUFACTURER=unknown" >> "$GITHUB_ENV"
- name: Testing xsimd
run: qemu-aarch64 --cpu max,sve${{ matrix.vector_bits }}=on -L /usr/aarch64-linux-gnu/ ./test/test_xsimd
working-directory: ${{ github.workspace }}/_build
19 changes: 14 additions & 5 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
name: Linux build
name: Linux x86 build
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash -l {0}

jobs:
build:
runs-on: ubuntu-latest
name: '${{ matrix.sys.compiler }} ${{ matrix.sys.version }} - ${{ matrix.sys.flags }}'
strategy:
matrix:
sys:
- { compiler: 'gcc', version: '12', flags: 'force_no_instr_set' }
- { compiler: 'gcc', version: '13', flags: 'enable_xtl_complex' }
- { compiler: 'gcc', version: '14', flags: 'avx' }
- { compiler: 'gcc', version: '12', flags: 'force_no_instr_set' }
- { compiler: 'gcc', version: '13', flags: 'enable_xtl_complex' }
- { compiler: 'gcc', version: '14', flags: 'avx' }
- { compiler: 'gcc', version: '13', flags: 'avx512' }
- { compiler: 'gcc', version: '10', flags: 'avx512' }
- { compiler: 'gcc', version: '12', flags: 'i386' }
- { compiler: 'gcc', version: '13', flags: 'avx512pf' }
- { compiler: 'gcc', version: '13', flags: 'avx512vbmi' }
- { compiler: 'gcc', version: '14', flags: 'avx512vbmi2' }
- { compiler: 'gcc', version: '13', flags: 'avx512vnni' }
- { compiler: 'clang', version: '16', flags: 'force_no_instr_set' }
- { compiler: 'clang', version: '16', flags: 'force_no_instr_set' }
- { compiler: 'clang', version: '16', flags: 'enable_xtl_complex' }
- { compiler: 'clang', version: '17', flags: 'avx' }
- { compiler: 'clang', version: '17', flags: 'sse3' }
Expand Down Expand Up @@ -119,9 +120,17 @@ jobs:
run: cmake --build _build
- name: Test
run: |
# Set CPU feature test expectations, 0 is explicit absence of the feature
export XSIMD_TEST_CPU_ASSUME_NEON64="0"
cd _build/test
if echo '${{ matrix.sys.flags }}' | grep -q 'avx512' ; then
# Running with emulation, must have AVX512, lower tier are checked by implications in tests
export XSIMD_TEST_CPU_ASSUME_AVX512F="1"
../../sde-external-9.48.0-2024-11-25-lin/sde64 -tgl -- ./test_xsimd
else
export XSIMD_TEST_CPU_ASSUME_SSE4_2=$(grep -q 'sse4_2' /proc/cpuinfo && echo "1" || echo "0")
export XSIMD_TEST_CPU_ASSUME_AVX=$(grep -q 'avx' /proc/cpuinfo && echo "1" || echo "0")
export XSIMD_TEST_CPU_ASSUME_AVX512F=$(grep -q 'avx512f' /proc/cpuinfo && echo "1" || echo "0")
export XSIMD_TEST_CPU_ASSUME_MANUFACTURER="intel,amd"
./test_xsimd
fi
12 changes: 12 additions & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
Expand All @@ -21,5 +22,16 @@ jobs:
run: cmake --build _build --verbose
- name: Testing sequential
run: cmake --build _build --target xbenchmark --verbose
- name: Set CPU feature test expectations
run: |
if echo '${{ matrix.os }}' | grep -q intel; then
echo "XSIMD_TEST_CPU_ASSUME_NEON64=0" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_SSE4_2=1" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_MANUFACTURER=intel" >> "$GITHUB_ENV"
else
echo "XSIMD_TEST_CPU_ASSUME_NEON64=1" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_SSE4_2=0" >> "$GITHUB_ENV"
echo "XSIMD_TEST_CPU_ASSUME_MANUFACTURER=unknown" >> "$GITHUB_ENV"
fi
- name: Testing xsimd
run: ${{github.workspace}}/_build/test/test_xsimd
15 changes: 14 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
build-windows-x86:
name: 'MSVC ${{ matrix.os }}, ${{ matrix.target }} ${{ matrix.sys.set }}'
defaults:
run:
Expand Down Expand Up @@ -48,6 +49,13 @@ jobs:
run: cmake --build _build
- name: Testing xsimd
if: ${{ !startsWith(matrix.sys.set, 'AVX512') }}
env:
# Set CPU feature test expectations
# Assuming the runner always has AVX2 (independent of compilation option)
XSIMD_TEST_CPU_ASSUME_NEON64: "0"
XSIMD_TEST_CPU_ASSUME_SSE4_2: "1"
XSIMD_TEST_CPU_ASSUME_AVX2: "1"
XSIMD_TEST_CPU_ASSUME_MANUFACTURER: "intel,amd"
run: ./_build/test/test_xsimd

build-windows-mingw:
Expand Down Expand Up @@ -106,4 +114,9 @@ jobs:
- name: Build
run: cmake --build _build
- name: Testing xsimd
env:
# Set CPU feature test expectations
XSIMD_TEST_CPU_ASSUME_NEON64: "1"
XSIMD_TEST_CPU_ASSUME_SSE4_2: "0"
XSIMD_TEST_CPU_ASSUME_MANUFACTURER: "unknown"
run: ./_build/test/test_xsimd
27 changes: 27 additions & 0 deletions include/xsimd/config/xsimd_cpu_features_x86.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,33 @@ namespace xsimd
return x86_manufacturer::unknown;
};

/** Return a string representation of an @ref x86_manufacturer value. */
constexpr const char* x86_manufacturer_name(x86_manufacturer m) noexcept
{
switch (m)
{
case x86_manufacturer::intel:
return "intel";
case x86_manufacturer::amd:
return "amd";
case x86_manufacturer::via:
return "via";
case x86_manufacturer::zhaoxin:
return "zhaoxin";
case x86_manufacturer::hygon:
return "hygon";
case x86_manufacturer::transmeta:
return "transmeta";
case x86_manufacturer::elbrus:
return "elbrus";
case x86_manufacturer::microsoft_vpc:
return "microsoft_vpc";
case x86_manufacturer::unknown:
return "unknown";
}
return "invalid";
}

struct x86_cpuid_leaf1_traits
{
static constexpr detail::x86_reg32_t leaf = 1;
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ set(XSIMD_TESTS
test_complex_power.cpp
test_complex_trigonometric.cpp
test_conversion.cpp
test_cpu_features.cpp
test_custom_default_arch.cpp
test_error_gamma.cpp
test_explicit_batch_instantiation.cpp
Expand Down
156 changes: 156 additions & 0 deletions test/test_cpu_features.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/***************************************************************************
* Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and *
* Martin Renou *
* Copyright (c) QuantStack *
* Copyright (c) Serge Guelton *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/

#include <algorithm>
#include <array>
#include <cstdlib>
#include <string>

#include <doctest/doctest.h>

#include "xsimd/xsimd.hpp"

#define CHECK_IMPLICATION(a, b) CHECK_UNARY(!(a) || (b))

namespace detail
{
void check_env_flag(const char* env_var, const char* feature_name, bool actual)
{
if (const char* val = std::getenv(env_var))
{
// Doctest struggles with string literals and const char *
// TODO(c++20): use std::format
auto msg = std::string(env_var) + " = " + val + ", " + feature_name + " = " + (actual ? "true" : "false");
INFO(msg);
CHECK_EQ(actual, val[0] == '1');
}
}

// TODO(c++23): use str.contains
bool contains(const std::string& haystack, const char* needle)
{
return haystack.find(needle) != std::string::npos;
}
}

#define CHECK_ENV_FEATURE(env_var, feature) detail::check_env_flag(env_var, #feature, feature)

/**
* Tests that x86_cpu_features respects the architectural implication chains.
*
* These are "always true" assertions: if a higher feature is reported, all
* features it architecturally implies must also be reported. The test reads
* the current CPU's features at runtime and verifies every implication.
*/
TEST_CASE("[cpu_features] x86 implication chains")
{
xsimd::x86_cpu_features cpu;

// SSE implication chain
CHECK_IMPLICATION(cpu.sse4_2(), cpu.sse4_1());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that make sense to use this to also check the is_base_of type relationship?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you have in mind, could you provide an example?

CHECK_IMPLICATION(cpu.sse4_1(), cpu.ssse3());
CHECK_IMPLICATION(cpu.ssse3(), cpu.sse3());
CHECK_IMPLICATION(cpu.sse3(), cpu.sse2());

// AVX implication chain
CHECK_IMPLICATION(cpu.avx(), cpu.sse4_2());
CHECK_IMPLICATION(cpu.avx2(), cpu.avx());
CHECK_IMPLICATION(cpu.fma4(), cpu.avx());
CHECK_IMPLICATION(cpu.fma3(), cpu.avx());

// AVX-512 iplication chain
CHECK_IMPLICATION(cpu.avx512f(), cpu.avx2());
CHECK_IMPLICATION(cpu.avx512dq(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512ifma(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512pf(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512er(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512cd(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512bw(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512vbmi(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512vbmi2(), cpu.avx512f());
CHECK_IMPLICATION(cpu.avx512vnni_bw(), cpu.avx512bw());
CHECK_IMPLICATION(cpu.avxvnni(), cpu.avx2());
}

TEST_CASE("[cpu_features] x86 manufacturer from environment")
{
xsimd::x86_cpu_features cpu;

const char* val = std::getenv("XSIMD_TEST_CPU_ASSUME_MANUFACTURER");
if (val)
{
struct entry
{
const char* name;
xsimd::x86_manufacturer value;
};
std::array<entry, 9> manufacturers = { {
{ "intel", xsimd::x86_manufacturer::intel },
{ "amd", xsimd::x86_manufacturer::amd },
{ "via", xsimd::x86_manufacturer::via },
{ "zhaoxin", xsimd::x86_manufacturer::zhaoxin },
{ "hygon", xsimd::x86_manufacturer::hygon },
{ "transmeta", xsimd::x86_manufacturer::transmeta },
{ "elbrus", xsimd::x86_manufacturer::elbrus },
{ "microsoft_vpc", xsimd::x86_manufacturer::microsoft_vpc },
{ "unknown", xsimd::x86_manufacturer::unknown },
} };

auto manufacturer = cpu.known_manufacturer();
const std::string allowed(val);
bool match = std::any_of(manufacturers.begin(), manufacturers.end(), [&](const entry& e)
{ return e.value == manufacturer && detail::contains(allowed, e.name); });

auto const msg = std::string("XSIMD_TEST_CPU_ASSUME_MANUFACTURER = ") + val
+ ", actual = " + xsimd::x86_manufacturer_name(manufacturer);
INFO(msg);
CHECK_UNARY(match);
}
}

TEST_CASE("[cpu_features] x86 features from environment")
{
xsimd::x86_cpu_features cpu;

CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SSE2", cpu.sse2());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SSE3", cpu.sse3());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SSSE3", cpu.ssse3());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SSE4_1", cpu.sse4_1());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SSE4_2", cpu.sse4_2());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_FMA3", cpu.fma3());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_FMA4", cpu.fma4());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX", cpu.avx());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX2", cpu.avx2());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX512F", cpu.avx512f());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX512BW", cpu.avx512bw());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX512CD", cpu.avx512cd());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVX512DQ", cpu.avx512dq());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_AVXVNNI", cpu.avxvnni());
}

TEST_CASE("[cpu_features] arm implication chains")
{
xsimd::arm_cpu_features cpu;

CHECK_IMPLICATION(cpu.neon64(), cpu.neon());
CHECK_IMPLICATION(cpu.sve(), cpu.neon64());
CHECK_IMPLICATION(cpu.i8mm(), cpu.neon64());
}

TEST_CASE("[cpu_features] arm features from environment")
{
xsimd::arm_cpu_features cpu;

CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_NEON", cpu.neon());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_NEON64", cpu.neon64());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_SVE", cpu.sve());
CHECK_ENV_FEATURE("XSIMD_TEST_CPU_ASSUME_I8MM", cpu.i8mm());
}
Loading