Skip to content
Merged
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
111 changes: 90 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: SimdLib CI

on:
push:
branches:
- master
pull_request:
workflow_dispatch:

Expand All @@ -18,8 +20,6 @@ jobs:
run: tools/Build.ps1 -Scope Native -Compiler Msvc
- name: Test the exact MSVC build receipt
run: tools/Run-Tests.ps1 -Scope Native -Compiler Msvc
- name: Build MSVC benchmark artifacts explicitly
run: tools/Build-Benchmarks.ps1 -Scope Native -Compiler Msvc
- name: Upload MSVC evidence
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -29,7 +29,6 @@ jobs:
out/pipeline/windows-msvc/**/provenance
out/pipeline/windows-msvc/**/reports
out/pipeline/windows-msvc/**/validation-build.manifest
out/pipeline/windows-msvc/**/benchmark-build.manifest
out/pipeline/windows-msvc/**/build/register-codegen/**/*.json
out/pipeline/windows-msvc/**/build/register-codegen/**/*.txt
out/pipeline/windows-msvc/**/build/method-flags-codegen/**/*.json
Expand All @@ -38,29 +37,77 @@ jobs:
out/pipeline/provenance
if-no-files-found: error

native-clangcl:
name: clang-cl and Clang coverage x64 validation
windows-clang20-container:
name: Windows clang-cl 20 container validation
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Build every Clang validation cell
- name: Build the Windows clang-cl 20 container and validation cell
run: tools/Run-WindowsClang20Container.ps1 -Action Build
- name: Test the exact clang-cl 20 build receipt
run: tools/Run-WindowsClang20Container.ps1 -Action Test -SkipImageBuild
- name: Upload clang-cl 20 evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: clang20-evidence
path: |
out/pipeline/windows-clangcl/**/provenance
out/pipeline/windows-clangcl/**/reports
out/pipeline/windows-clangcl/**/validation-build.manifest
out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt
out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.txt
out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.txt
out/pipeline/logs
out/pipeline/provenance
if-no-files-found: error

native-clang22:
name: clang-cl 22 and Clang 22 coverage x64 validation
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- name: Install the pinned Windows toolchain with Chocolatey
shell: pwsh
run: |
choco upgrade cmake --version=4.4.0 --yes --allow-downgrade --no-progress --limit-output
if ($LASTEXITCODE -notin @(0, 1605, 1614, 1641, 3010)) {
throw "Chocolatey failed to install CMake 4.4.0 with exit code $LASTEXITCODE"
}
choco upgrade llvm --version=22.1.7 --yes --allow-downgrade --no-progress --limit-output
if ($LASTEXITCODE -notin @(0, 1605, 1614, 1641, 3010)) {
throw "Chocolatey failed to install LLVM 22.1.7 with exit code $LASTEXITCODE"
}
'C:\Program Files\CMake\bin' | Out-File -Encoding utf8 -Append $env:GITHUB_PATH
'C:\Program Files\LLVM\bin' | Out-File -Encoding utf8 -Append $env:GITHUB_PATH
- name: Verify the pinned Windows toolchain
run: |
$cmakeVersion = & 'C:\Program Files\CMake\bin\cmake.exe' --version | Select-Object -First 1
if ($cmakeVersion -ne 'cmake version 4.4.0') { throw "Unexpected CMake version: $cmakeVersion" }
$version = & 'C:\Program Files\LLVM\bin\clang-cl.exe' --version | Select-Object -First 1
if ($version -notmatch 'clang version 22\.1\.7\b') { throw "Unexpected clang-cl version: $version" }
$cmakeVersion
$version
- name: Build every Clang 22 validation cell
run: tools/Build.ps1 -Scope Native -Compiler ClangCl,ClangCoverage
- name: Test the exact Clang build receipt
- name: Test the exact Clang 22 build receipt
run: tools/Run-Tests.ps1 -Scope Native -Compiler ClangCl,ClangCoverage
- name: Build clang-cl benchmark artifacts explicitly
run: tools/Build-Benchmarks.ps1 -Scope Native -Compiler ClangCl
- name: Upload Clang evidence
- name: Upload Clang 22 evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: clang-evidence
name: clang22-evidence
path: |
out/pipeline/windows-clangcl/**/provenance
out/pipeline/windows-clangcl/**/reports
out/pipeline/windows-clangcl/**/validation-build.manifest
out/pipeline/windows-clangcl/**/benchmark-build.manifest
out/pipeline/windows-clangcl/**/build/register-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/register-codegen/**/*.txt
out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/partial-register-codegen/**/*.txt
out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.json
out/pipeline/windows-clangcl/**/build/method-flags-codegen/**/*.txt
out/pipeline/windows-clang-coverage/**/provenance
Expand All @@ -73,8 +120,21 @@ jobs:
if-no-files-found: error

container-compilers:
name: GCC 13, GCC 14, and Clang 22 containers
name: ${{ matrix.name }} container validation
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- compiler: Gcc13
service: gcc13
name: GCC 13
- compiler: Gcc14
service: gcc14
name: GCC 14
- compiler: Clang22
service: clang22
name: Clang 22
steps:
- uses: actions/checkout@v4
- name: Install required Docker Compose
Expand All @@ -96,25 +156,34 @@ jobs:
test "${actual_sha256}" = "${expected_sha256}"
chmod +x "${plugin_dir}/docker-compose"
docker compose version
- name: Build every Linux validation cell
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build cached compiler image
uses: docker/build-push-action@v6
with:
context: .
file: containers/Dockerfile.${{ matrix.service }}
tags: simdlib/${{ matrix.service }}:local
load: true
provenance: false
# Include intermediate stages so the compiled CMake toolchain is reusable.
cache-from: type=gha,version=2,scope=simdlib-${{ matrix.service }}
cache-to: type=gha,version=2,scope=simdlib-${{ matrix.service }},mode=max,ignore-error=true
- name: Build selected Linux validation cells
shell: pwsh
run: tools/Build.ps1 -Scope Containers
run: tools/Build.ps1 -Scope Containers -Compiler ${{ matrix.compiler }} -SkipImageBuild
- name: Test the exact Linux build receipt
shell: pwsh
run: tools/Run-Tests.ps1 -Scope Containers
- name: Build Linux benchmark artifacts explicitly
shell: pwsh
run: tools/Build-Benchmarks.ps1 -Scope Containers
run: tools/Run-Tests.ps1 -Scope Containers -Compiler ${{ matrix.compiler }}
- name: Upload container evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-container-evidence
name: linux-${{ matrix.service }}-evidence
path: |
out/pipeline/linux-*/**/provenance
out/pipeline/linux-*/**/reports
out/pipeline/linux-*/**/validation-build.manifest
out/pipeline/linux-*/**/benchmark-build.manifest
out/pipeline/linux-*/**/build/register-codegen/**/*.json
out/pipeline/linux-*/**/build/register-codegen/**/*.txt
out/pipeline/linux-*/**/build/method-flags-codegen/**/*.json
Expand Down
4 changes: 2 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"group": {
"kind": "build",
"isDefault": true
"isDefault": false
},
"detail": "Builds the complete native and container validation matrix without benchmark artifacts."
},
Expand Down Expand Up @@ -103,7 +103,7 @@
"args": [
"-NoProfile",
"-Command",
"& clang-format -i --style=file --fallback-style=none @(& git ls-files -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.h' '*.hh' '*.hpp' '*.hxx' '*.inl' '*.ipp' '*.cu' '*.cuh')"
"$clangFormat = (Get-Command clang-format -ErrorAction SilentlyContinue).Source; if (-not $clangFormat) { $llvmFormat = Join-Path $env:ProgramFiles 'LLVM\\bin\\clang-format.exe'; if (Test-Path $llvmFormat) { $clangFormat = $llvmFormat } }; if (-not $clangFormat) { throw 'clang-format was not found on PATH or in C:\\Program Files\\LLVM\\bin' }; & $clangFormat -i --style=file --fallback-style=none @(git ls-files -- '*.c' '*.cc' '*.cpp' '*.cxx' '*.h' '*.hh' '*.hpp' '*.hxx' '*.inl' '*.ipp' '*.cu' '*.cuh')"
],
"options": {
"cwd": "${workspaceFolder}"
Expand Down
27 changes: 26 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ cmake_minimum_required(VERSION 3.31)

project(SimdLib VERSION 0.2.0 LANGUAGES CXX)

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

add_library(SimdLib INTERFACE)
add_library(SimdLib::SimdLib ALIAS SimdLib)
target_compile_features(SimdLib INTERFACE cxx_std_20)
target_include_directories(SimdLib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_sources(SimdLib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/visualizers/SimdLib.natvis>)

Expand Down Expand Up @@ -40,6 +44,27 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
endif()
set_property(TARGET SimdLibRegister PROPERTY
SIMDLIB_REGISTER_COMPILER_SUPPORTED ${SIMDLIB_REGISTER_COMPILER_SUPPORTED})
set_property(TARGET SimdLibRegister PROPERTY EXPORT_NAME Register)

install(TARGETS SimdLib SimdLibRegister EXPORT SimdLibTargets)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT SimdLibTargets
FILE SimdLibTargets.cmake
NAMESPACE SimdLib::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib)

configure_package_config_file(
cmake/SimdLibConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/SimdLibConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimdLib)

if(PROJECT_IS_TOP_LEVEL)
include(cmake/development/Development.cmake)
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
SimdLib is a small, header-only library for working with SIMD data and bit-heavy
code without scattering compiler intrinsics throughout your project. Its core
surface remains C++20; supporting C++23 translation units can additionally use
the complete-register value interface.
complete- and partial-register value interfaces.

There is no library binary to build or ship. Add the headers to your project,
link the CMake interface target, and use only the pieces you need.
Expand All @@ -16,6 +16,11 @@ link the CMake interface target, and use only the pieces you need.
stable storage and ABI contracts.
- `RegisterMask<T, Bits>` preserves native comparison predicates and provides
composition, reduction, observation, and selection operations.
- `PartialRegister<T, Bits, Active>` represents a compile-time contiguous
low-lane prefix in one native register and keeps its inactive suffix
all-bits-zero.
- `PartialRegisterMask<T, Bits, Active>` is the matching active-prefix
predicate type.
- `NativeApi<T>` and `Api<Bits, T>` remain supported for C++20, compatibility,
specialized low-level access, collection helpers, and operations intentionally
excluded from `Register`.
Expand Down Expand Up @@ -306,6 +311,12 @@ fixtures retain normal `/GS` protection and paired disassembly for review.
configuration details, formatting, and development commands.
- [Public namespace and compatibility](docs/PublicNamespace.md) describes the
supported API boundary.
- [Register contract and qualification](docs/RegisterContract.md) records the
complete-register semantics; [RegisterQualification.md](docs/RegisterQualification.md)
records its compiler, ABI, generated-code, and validation boundaries.
- [PartialRegister design and qualification](docs/PartialRegisterQualification.md)
records the active-prefix contract, result rules, examples, compiler matrix,
and validation evidence.
- [Build and validation](docs/BuildPipeline.md) documents the supported build,
test, compiler-matrix, and reporting commands.

Expand Down
50 changes: 50 additions & 0 deletions benchmarks/Register.benchmarks.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <SimdLib/PartialRegister.h>
#include <SimdLib/Register.h>

#include <catch2/benchmark/catch_benchmark.hpp>
Expand Down Expand Up @@ -58,6 +59,55 @@ template <std::size_t count> [[nodiscard]] std::array<std::uint32_t, count> make

} // namespace

TEST_CASE("PartialRegister invariant-maintenance benchmarks", "[simdlib][benchmark][partial-register]")
{
using partial128 = SimdLib::PartialRegister<float, 128, 3>;
using partial256 = SimdLib::PartialRegister<float, 256, 5>;
using integer_partial256 = SimdLib::PartialRegister<std::uint32_t, 256, 5>;
auto seed = runtime_seed();
const auto lhs128_lanes = make_float_lanes<partial128::lane_count>(seed);
const auto rhs128_lanes = make_float_lanes<partial128::lane_count>(seed);
const auto lhs256_lanes = make_float_lanes<partial256::lane_count>(seed);
const auto rhs256_lanes = make_float_lanes<partial256::lane_count>(seed);
const auto integer_lhs_lanes = make_unsigned_lanes<integer_partial256::lane_count>(seed);
const auto integer_rhs_lanes = make_unsigned_lanes<integer_partial256::lane_count>(seed);
const auto lhs128 = partial128::load(std::span<const float, partial128::lane_count>{lhs128_lanes});
const auto rhs128 = partial128::load(std::span<const float, partial128::lane_count>{rhs128_lanes});
const auto lhs256 = partial256::load(std::span<const float, partial256::lane_count>{lhs256_lanes});
const auto rhs256 = partial256::load(std::span<const float, partial256::lane_count>{rhs256_lanes});
const auto integer_lhs = integer_partial256::load(std::span<const std::uint32_t, integer_partial256::lane_count>{integer_lhs_lanes});
const auto integer_rhs = integer_partial256::load(std::span<const std::uint32_t, integer_partial256::lane_count>{integer_rhs_lanes});

BENCHMARK("PartialRegister 128-bit three-lane add")
{
return (lhs128 + rhs128).native;
};
BENCHMARK("Raw Api 128-bit canonical three-lane add")
{
return partial128::api_type::add(lhs128.native, rhs128.native);
};
BENCHMARK("PartialRegister 256-bit five-lane add")
{
return (lhs256 + rhs256).native;
};
BENCHMARK("Raw Api 256-bit canonical five-lane add")
{
return partial256::api_type::add(lhs256.native, rhs256.native);
};
BENCHMARK("PartialRegister 256-bit five-lane division")
{
return (integer_lhs / integer_rhs).native;
};
BENCHMARK("PartialRegister 256-bit five-lane compare and select")
{
return lhs256.compare_greater(rhs256).select(lhs256, rhs256).native;
};
BENCHMARK("PartialRegister 256-bit five-lane shuffle")
{
return lhs256.template shuffle<4, 3, 2, 1, 0>().native;
};
}

TEST_CASE("Register runtime-derived wrapper and raw benchmarks", "[simdlib][benchmark][register]")
{
using register128 = SimdLib::Register<float, 128>;
Expand Down
2 changes: 2 additions & 0 deletions cmake/CheckPublicConsumerBoundary.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ file(GLOB_RECURSE public_consumer_sources
"${SOURCE_DIRECTORY}/tests/consumer/*.cpp"
"${SOURCE_DIRECTORY}/tests/format_odr/*.cpp"
"${SOURCE_DIRECTORY}/tests/headers/*.cpp"
"${SOURCE_DIRECTORY}/tests/installed_consumer/*.cpp"
"${SOURCE_DIRECTORY}/tests/partial_register_odr/*.cpp"
"${SOURCE_DIRECTORY}/tests/register_odr/*.cpp"
"${SOURCE_DIRECTORY}/tests/smoke/*.cpp")
list(SORT public_consumer_sources)
Expand Down
Loading
Loading