diff --git a/.github/workflows/celerity_ci_windows.yml b/.github/workflows/celerity_ci_windows.yml new file mode 100644 index 000000000..ebe846b01 --- /dev/null +++ b/.github/workflows/celerity_ci_windows.yml @@ -0,0 +1,133 @@ +name: Celerity Windows CI + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + +jobs: + build-and-test-windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + compiler: [clang-cl] # future proofing: potentially add more compilers in the future like msvc... + build_type: [Debug, Release] + defaults: + run: + shell: pwsh + env: + SIMSYCL_DIR: ${{ github.workspace }}\SimSYCL + SIMSYCL_INSTALL_DIR: ${{ github.workspace }}\simsycl-install + SIMSYCL_SYSTEM: ${{ github.workspace }}\ci\simsycl-system.json + EXTRA_LINKER_FLAGS: "" + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up MSVC developer environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Locate preinstalled LLVM + if: matrix.compiler == 'clang-cl' + run: | + $llvmDir = "C:\Program Files\LLVM" + $clangMajor = (Get-ChildItem "$llvmDir\lib\clang").Name + clang-cl.exe --version + "LIB=$llvmDir\lib\clang\$clangMajor\lib\windows;$env:LIB" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "EXTRA_LINKER_FLAGS=clang_rt.builtins-x86_64.lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Locate preinstalled vcpkg + run: | + "BOOST_PREFIX_PATH=$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows-static-md" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + $commit = git -C "$env:VCPKG_INSTALLATION_ROOT" rev-parse --short HEAD + "VCPKG_COMMIT=$commit" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Cache vcpkg-built Boost.Context + id: cache-boost + uses: actions/cache@v4 + with: + path: ${{ env.BOOST_PREFIX_PATH }} + key: windows-vcpkg-boost-context-${{ env.VCPKG_COMMIT }} + + - name: Build Boost.Context via vcpkg + if: steps.cache-boost.outputs.cache-hit != 'true' + run: '& "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe" install boost-context:x64-windows-static-md' + + - name: Resolve SimSYCL commit + run: | + $sha = (git ls-remote https://github.com/celerity/SimSYCL.git HEAD).Split("`t")[0] + "SIMSYCL_COMMIT=$sha" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Cache SimSYCL install + id: cache-simsycl + uses: actions/cache@v4 + with: + path: ${{ env.SIMSYCL_INSTALL_DIR }} + key: windows-simsycl-${{ matrix.compiler }}-${{ matrix.build_type }}-${{ env.SIMSYCL_COMMIT }} + + - name: Clone SimSYCL + if: steps.cache-simsycl.outputs.cache-hit != 'true' + run: git clone --depth 1 https://github.com/celerity/SimSYCL.git "${{ env.SIMSYCL_DIR }}" + + - name: Configure SimSYCL + if: steps.cache-simsycl.outputs.cache-hit != 'true' + run: > + cmake -S "${{ env.SIMSYCL_DIR }}" -B "${{ env.SIMSYCL_DIR }}\build" -G Ninja + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_C_COMPILER=${{ matrix.compiler }}.exe + -DCMAKE_CXX_COMPILER=${{ matrix.compiler }}.exe + -DCMAKE_PREFIX_PATH="${{ env.BOOST_PREFIX_PATH }}" + -DCMAKE_INSTALL_PREFIX="${{ env.SIMSYCL_INSTALL_DIR }}" + "-DCMAKE_EXE_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + "-DCMAKE_SHARED_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + "-DCMAKE_MODULE_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + + - name: Build SimSYCL + if: steps.cache-simsycl.outputs.cache-hit != 'true' + run: cmake --build "${{ env.SIMSYCL_DIR }}\build" + + - name: Run SimSYCL's own test suite + if: steps.cache-simsycl.outputs.cache-hit != 'true' + run: '& "${{ env.SIMSYCL_DIR }}\build\test\tests.exe" --reporter compact' + + - name: Install SimSYCL + if: steps.cache-simsycl.outputs.cache-hit != 'true' + run: cmake --install "${{ env.SIMSYCL_DIR }}\build" + + - name: Configure Celerity + run: > + cmake -S "${{ github.workspace }}" -B "${{ github.workspace }}\build" -G Ninja + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_C_COMPILER=${{ matrix.compiler }}.exe + -DCMAKE_CXX_COMPILER=${{ matrix.compiler }}.exe + -DCMAKE_PREFIX_PATH="${{ env.SIMSYCL_INSTALL_DIR }};${{ env.BOOST_PREFIX_PATH }}" + -DCELERITY_SYCL_IMPL=SimSYCL + -DCELERITY_ENABLE_MPI=OFF + -DCELERITY_USE_MIMALLOC=OFF + "-DCMAKE_EXE_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + "-DCMAKE_SHARED_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + "-DCMAKE_MODULE_LINKER_FLAGS=${{ env.EXTRA_LINKER_FLAGS }}" + + - name: Build Celerity + run: cmake --build "${{ github.workspace }}\build" + + - name: Run unit tests + run: '& "${{ github.workspace }}\build\test\all_tests.exe" --reporter compact' + + - name: Upload build logs (on failure) + if: failure() + uses: actions/upload-artifact@v4 + with: + name: windows-${{ matrix.compiler }}-${{ matrix.build_type }}-simsycl-build-logs + path: | + ${{ github.workspace }}\build\CMakeFiles\CMakeError.log + ${{ github.workspace }}\build\CMakeFiles\CMakeOutput.log + ${{ env.SIMSYCL_DIR }}\build\CMakeFiles\CMakeError.log + ${{ env.SIMSYCL_DIR }}\build\CMakeFiles\CMakeOutput.log + if-no-files-found: ignore diff --git a/CHANGELOG.md b/CHANGELOG.md index e5d560f01..2c95f86b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This release is WIP ### Internal - Adapt tests for Windows by disabling implementation-dependent graph printing tests and increasing tolerances for executor timing tests (#337) +- Add automated CI compilation and testing of Celerity for Windows with clang-cl (#341) ## [0.7.0] - 2025-08-18 diff --git a/CMakeLists.txt b/CMakeLists.txt index bf8def0ff..1f5b588ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,14 +158,35 @@ macro(fetch_content_from_submodule DEPNAME RELPATH) endif() endmacro() +function(mark_include_dirs_system TARGET_NAME) + if(TARGET ${TARGET_NAME}) + set(CELERITY_DETAIL_REAL_TARGET ${TARGET_NAME}) + get_target_property(CELERITY_DETAIL_ALIASED_TARGET ${TARGET_NAME} ALIASED_TARGET) + if(CELERITY_DETAIL_ALIASED_TARGET) + set(CELERITY_DETAIL_REAL_TARGET ${CELERITY_DETAIL_ALIASED_TARGET}) + endif() + get_target_property(CELERITY_DETAIL_SYSTEM_INCLUDES ${CELERITY_DETAIL_REAL_TARGET} INTERFACE_INCLUDE_DIRECTORIES) + if(CELERITY_DETAIL_SYSTEM_INCLUDES) + set_target_properties(${CELERITY_DETAIL_REAL_TARGET} PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${CELERITY_DETAIL_SYSTEM_INCLUDES}") + endif() + endif() +endfunction() + +if (CELERITY_SYCL_IMPL STREQUAL "SimSYCL") +mark_include_dirs_system(SimSYCL::simsycl) +endif() + set(FMT_INSTALL ON CACHE BOOL "" FORCE) fetch_content_from_submodule(fmt vendor/fmt) +mark_include_dirs_system(fmt::fmt) set(SPDLOG_INSTALL ON CACHE BOOL "" FORCE) set(SPDLOG_FMT_EXTERNAL ON CACHE BOOL "" FORCE) fetch_content_from_submodule(spdlog vendor/spdlog) +mark_include_dirs_system(spdlog::spdlog) fetch_content_from_submodule(small_vector vendor/small_vector) +mark_include_dirs_system(gch::small_vector) if(WIN32) # Catch2 uses custom top-level exception filters that conflict with MSMPI, which also uses their own filters. @@ -174,9 +195,12 @@ if(WIN32) endif() fetch_content_from_submodule(Catch2 vendor/Catch2) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) +mark_include_dirs_system(Catch2::Catch2) +mark_include_dirs_system(Catch2::Catch2WithMain) set(LIBENVPP_INSTALL ON CACHE BOOL "" FORCE) fetch_content_from_submodule(libenvpp vendor/libenvpp) +mark_include_dirs_system(libenvpp::libenvpp) if(CELERITY_USE_MIMALLOC) set(MI_OVERRIDE ON CACHE BOOL "" FORCE) @@ -185,6 +209,7 @@ if(CELERITY_USE_MIMALLOC) set(MI_BUILD_OBJECT OFF CACHE BOOL "" FORCE) set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE) fetch_content_from_submodule(mimalloc vendor/mimalloc) + mark_include_dirs_system(mimalloc) endif() if(CELERITY_TRACY_SUPPORT) @@ -200,6 +225,7 @@ if(CELERITY_TRACY_SUPPORT) endif() fetch_content_from_submodule(Tracy vendor/tracy) set(CMAKE_CXX_FLAGS "${PREVIOUS_CMAKE_CXX_FLAGS}") + mark_include_dirs_system(Tracy::TracyClient) endif() # Deprecated feature flags @@ -404,7 +430,10 @@ add_sycl_to_target( ) if(MSVC) - target_compile_options(celerity_runtime PRIVATE /MP /W3) + target_compile_options(celerity_runtime PRIVATE /W4 /wd4100 /we4715 /we4700) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + target_compile_options(celerity_runtime PRIVATE /MP) + endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") target_compile_options(celerity_runtime PRIVATE -Wall -Wextra -Wno-unused-parameter -Werror=return-type -Werror=init-self -Werror=undef) endif() diff --git a/docs/platform-support.md b/docs/platform-support.md index 8ab77569e..f48643591 100644 --- a/docs/platform-support.md +++ b/docs/platform-support.md @@ -45,6 +45,7 @@ Those are (CRT = Intel Compute Runtime, L0 = oneAPI Level Zero): | AdaptiveCpp | [`v24.06.0`](https://github.com/AdaptiveCpp/AdaptiveCpp/tree/v24.06.0) (Clang 14.0, CUDA 11.8.0) | Ubuntu 22.04 | NVIDIA RTX 2070 | Debug, Release | | AdaptiveCpp | [`HEAD`](https://github.com/AdaptiveCpp/AdaptiveCpp) (Clang 18.0, CUDA 12.5.0)\* | Ubuntu 24.04 | NVIDIA RTX 2070 | Debug, Release | | SimSYCL | [`HEAD`](https://github.com/celerity/SimSYCL) (GCC 13.2) | Ubuntu 24.04 | (None) | Debug, Release | +| SimSYCL | [`HEAD`](https://github.com/celerity/SimSYCL) (Clang 20.1.8) | Windows | (None) | Debug, Release | \* currently requires a patch for an illegal macro definition in CUDA: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 49826a163..caae99032 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,7 +15,10 @@ function(add_example NAME) ) if(MSVC) - target_compile_options("${NAME}" PRIVATE /D_CRT_SECURE_NO_WARNINGS /MP /W3) + target_compile_options("${NAME}" PRIVATE /D_CRT_SECURE_NO_WARNINGS /W4 /wd4100 /we4715 /we4700) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + target_compile_options("${NAME}" PRIVATE /MP) + endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") target_compile_options("${NAME}" PRIVATE -Wall -Wextra -Wno-unused-parameter) endif() diff --git a/src/backend/sycl_backend.cc b/src/backend/sycl_backend.cc index dff8cd7ed..c5b76ec8a 100644 --- a/src/backend/sycl_backend.cc +++ b/src/backend/sycl_backend.cc @@ -186,7 +186,7 @@ sycl_backend::sycl_backend(const std::vector& devices, const confi for(device_id did = 0; did < m_impl->system.devices.size(); ++did) { m_impl->devices[did].submission_thread.emplace(named_threads::task_type_device_submitter(did.value), m_impl->config.profiling); // no need to wait for the event -> will happen before the first task is submitted - (void)m_impl->devices[did].submission_thread->submit([did] { closure_hydrator::make_available(); }); + (void)m_impl->devices[did].submission_thread->submit([] { closure_hydrator::make_available(); }); } } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4caf673be..8eb1e45ed 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,7 +12,10 @@ function(set_test_target_parameters TARGET SOURCE) set_property(TARGET ${TARGET} PROPERTY POSITION_INDEPENDENT_CODE ON) if(MSVC) - target_compile_options(${TARGET} PRIVATE /D_CRT_SECURE_NO_WARNINGS /MP /W3 /bigobj) + target_compile_options(${TARGET} PRIVATE /D_CRT_SECURE_NO_WARNINGS /W4 /wd4100 /wd4189 /we4715 /we4700 /bigobj) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + target_compile_options(${TARGET} PRIVATE /MP) + endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wextra -Wno-unused-parameter -Wno-unused-variable) endif() diff --git a/test/affinity_tests.cc b/test/affinity_tests.cc index 95457d5e2..e57e1e16a 100644 --- a/test/affinity_tests.cc +++ b/test/affinity_tests.cc @@ -148,15 +148,25 @@ TEST_CASE("a warning is emitted if insufficient cores are available", "[affinity } TEST_CASE("a warning is emitted if hardcoded threads are not available to this process", "[affinity]") { + const core_set process_mask = {0, 1, 2, 3, 4}; + if(!have_cores(process_mask)) { + SKIP("Skipping test because not all needed cores are available"); + return; + } test_utils::allow_max_log_level(detail::log_level::warn); - raii_affinity_masking mask({0, 1, 2, 3, 4}); + raii_affinity_masking mask(process_mask); detail::thread_pinning::thread_pinner pinner({.enabled = true, .use_backend_device_submission_threads = false, .hardcoded_core_ids = {4, 5, 6}}); CHECK(test_utils::log_contains_substring(detail::log_level::warn, "Not all hardcoded core IDs are available, downgrading to auto-pinning.")); } TEST_CASE("do not plan for device submission threads if they are unused", "[affinity]") { - raii_affinity_masking mask({1, 2, 3, 4}); + const core_set process_mask = {1, 2, 3, 4}; + if(!have_cores(process_mask)) { + SKIP("Skipping test because not all needed cores are available"); + return; + } + raii_affinity_masking mask(process_mask); const detail::thread_pinning::runtime_configuration cfg = {.enabled = true, .num_devices = 10, .use_backend_device_submission_threads = false}; detail::thread_pinning::thread_pinner pinner(cfg); SUCCEED(); // no additional check, a warning will make the test fail if we do not handle this case correctly diff --git a/test/command_graph_general_tests.cc b/test/command_graph_general_tests.cc index e65008b48..b347b10ce 100644 --- a/test/command_graph_general_tests.cc +++ b/test/command_graph_general_tests.cc @@ -345,7 +345,6 @@ TEST_CASE("all commands have a transitive true-dependency on the preceding epoch TEST_CASE("fences introduce dependencies on host objects", "[command_graph_generator][command-graph][fence]") { const size_t num_nodes = 2; - const range<1> node_range{num_nodes}; cdag_test_context cctx(num_nodes); @@ -362,7 +361,6 @@ TEST_CASE("fences introduce dependencies on host objects", "[command_graph_gener TEST_CASE("fences introduce dependencies on buffers", "[command_graph_generator][command-graph][fence]") { const size_t num_nodes = 2; - const range<1> node_range{num_nodes}; cdag_test_context cctx(num_nodes); diff --git a/test/dag_benchmarks.cc b/test/dag_benchmarks.cc index 566b27ad9..6161a51ed 100644 --- a/test/dag_benchmarks.cc +++ b/test/dag_benchmarks.cc @@ -402,7 +402,6 @@ template step(up, u); auto t = 0.0; - size_t i = 0; while(t < T) { step(up, u); std::swap(u, up); diff --git a/test/out_of_order_engine_tests.cc b/test/out_of_order_engine_tests.cc index dd0ef209d..c6194bba1 100644 --- a/test/out_of_order_engine_tests.cc +++ b/test/out_of_order_engine_tests.cc @@ -453,7 +453,7 @@ TEST_CASE("eagerly-assignable instructions become immediately assignable once th out_of_order_test_context octx(1); auto k1 = octx.device_kernel({}, device_id(0), /* priority */ 0); auto k2 = octx.device_kernel({k1}, device_id(0), /* priority */ 1); - auto k3 = octx.device_kernel({k1}, device_id(0), /* priority */ 0); + [[maybe_unused]] auto k3 = octx.device_kernel({k1}, device_id(0), /* priority */ 0); auto k4 = octx.device_kernel({k2}, device_id(0), /* priority */ 2); const auto first = octx.assign_one(); diff --git a/test/test_utils.cc b/test/test_utils.cc index b4e562f3b..249e92443 100644 --- a/test/test_utils.cc +++ b/test/test_utils.cc @@ -242,7 +242,9 @@ namespace celerity::test_utils_detail { const char* const expected_runtime_init_warnings_regex = "Celerity has detected that only .* logical cores are available to this process.*|" "Celerity detected more than one node \\(MPI rank\\) on this host, which is not recommended.*|" - "Instrumentation for profiling with Tracy is enabled\\. Performance may be negatively impacted\\.|"; + "Instrumentation for profiling with Tracy is enabled\\. Performance may be negatively impacted\\.|" + "Insufficient logical cores available for thread pinning \\(required .* starting from .*, .* " + "available\\), disabling pinning\\. Performance may be negatively impacted\\.|"; const char* const expected_device_enumeration_warnings_regex = "Found fewer devices .* than local nodes .*, multiple nodes will use the same device.*"; @@ -253,7 +255,7 @@ const char* const expected_backend_fallback_warnings_regex = const char* const expected_dry_run_executor_warnings_regex = "Encountered a \"fence\" command while \"CELERITY_DRY_RUN_NODES\" is set. The result of this " "operation will not match the expected output of an actual run."; -const char* const expected_executor_progress_warnings_regex = "\\[executor\\] no progress for .* s, might be stuck.*"; +[[maybe_unused]] const char* const expected_executor_progress_warnings_regex = "\\[executor\\] no progress for .* s, might be stuck.*"; const char* const expected_starvation_warning_regex = "The executor was starved for instructions for [0-9]+\\.[0-9] .{0,2}s, or [0-9]+\\.[0-9]% of the total active time of [0-9]+\\.[0-9] .{0,2}s. This may "