From dba5a2e057a1815d1f5af1267c56ea0dcf96600f Mon Sep 17 00:00:00 2001 From: Jhin Lee Date: Thu, 24 Sep 2026 10:16:40 -0400 Subject: [PATCH] fix: link Windows ARM64 Vulkan against the native ARM64 Vulkan SDK The v0.5.0 release retry (run 36004579792) failed only in the Windows arm64 Vulkan lane: lld-link : error : vulkan-1.lib(vulkan-1.dll): machine type x64 conflicts with arm64 That lane installed LunarG's x64 SDK, whose Lib/vulkan-1.lib is x64. The 09-23 VS 2022 run found the same x64 library and passed, so the new VS 2026 toolchain now rejects the mismatch that the old one accepted. LunarG ships a native ARM64 SDK at the pinned version under its "warm" platform. A shared composite action now installs the SDK matching the target arch. It checks each installer's SHA-256 against LunarG's file index and fails if vulkan-1.lib is not built for the target machine. native_release uses it for both arches; x64 keeps the same installer. validate_wrapper gains windows-arm64-vulkan, which runs the release build command on the VS 2026 arm64 image (pinned and v0.5.0) and checks that ggml-vulkan.dll is ARM64 and imports vulkan-1.dll. Until now PR CI never built this lane. --- .../install-vulkan-sdk-windows/action.yml | 45 +++++++++++++++++++ .github/workflows/native_release.yml | 9 ++-- .github/workflows/validate_wrapper.yml | 42 ++++++++++++++++- docs/platform_backend_strategy.md | 4 ++ tests/test_ci_scope.py | 2 +- tests/test_windows_vulkan_sdk.py | 31 +++++++++++++ tools/ci_scope.py | 2 +- 7 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 .github/actions/install-vulkan-sdk-windows/action.yml create mode 100644 tests/test_windows_vulkan_sdk.py diff --git a/.github/actions/install-vulkan-sdk-windows/action.yml b/.github/actions/install-vulkan-sdk-windows/action.yml new file mode 100644 index 0000000..d234d31 --- /dev/null +++ b/.github/actions/install-vulkan-sdk-windows/action.yml @@ -0,0 +1,45 @@ +name: Install Vulkan SDK (Windows) +description: Install the pinned LunarG Vulkan SDK built for the target architecture and export VULKAN_SDK. +inputs: + arch: + description: Target architecture, x64 or arm64 + required: true +runs: + using: composite + steps: + - name: Install Vulkan SDK + shell: pwsh + env: + SDK_ARCH: ${{ inputs.arch }} + run: | + $ver = "1.4.313.2" + # LunarG files the native ARM64 SDK under its "warm" platform. The x64 + # SDK's vulkan-1.lib is x64-only, and lld-link (VS 2026 ClangCL) refuses + # it for ARM64. Digests come from https://vulkan.lunarg.com/sdk/files.json. + switch ($env:SDK_ARCH) { + "x64" { + $url = "https://sdk.lunarg.com/sdk/download/$ver/windows/vulkansdk-windows-X64-$ver.exe" + $sha = "34a921d951858274ca8e470e9d0a3b7624db41216b3908ccea9f73c8a1b7500e" + $machine = "x64" + } + "arm64" { + $url = "https://sdk.lunarg.com/sdk/download/$ver/warm/vulkansdk-windows-ARM64-$ver.exe" + $sha = "29c3e7b9ff9c9d38455708bed7e744f879c770c15f3fedae55c409817c375515" + $machine = "ARM64" + } + default { throw "Unsupported Vulkan SDK arch: $env:SDK_ARCH" } + } + $installer = Join-Path $env:RUNNER_TEMP "VulkanSDK-Installer.exe" + curl.exe -fsSL -o $installer $url + if ($LASTEXITCODE -ne 0) { throw "Vulkan SDK download failed: $url" } + $actual = (Get-FileHash $installer -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actual -ne $sha) { throw "Vulkan SDK installer SHA-256 $actual does not match $sha" } + & $installer --accept-licenses --default-answer --confirm-command install + $sdk = "C:\VulkanSDK\$ver" + $lib = Join-Path $sdk "Lib\vulkan-1.lib" + if (-not (Test-Path $lib)) { throw "Missing $lib" } + # dumpbin comes from the job's MSVC developer environment. + $headers = (& dumpbin /headers $lib) -join "`n" + if ($headers -notmatch "\($machine\)") { throw "$lib is not a $machine import library" } + Add-Content $env:GITHUB_ENV "VULKAN_SDK=$sdk" + Add-Content $env:GITHUB_PATH "$sdk\Bin" diff --git a/.github/workflows/native_release.yml b/.github/workflows/native_release.yml index c4507da..bfd3c79 100644 --- a/.github/workflows/native_release.yml +++ b/.github/workflows/native_release.yml @@ -827,12 +827,9 @@ jobs: Add-Content $env:GITHUB_ENV "CMAKE_CXX_COMPILER_LAUNCHER=" - name: Install Vulkan SDK if: ${{ matrix.needs_vulkan }} - run: | - $ver = "1.4.313.2" - curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/$ver/windows/vulkansdk-windows-X64-$ver.exe" - & "$env:RUNNER_TEMP\VulkanSDK-Installer.exe" --accept-licenses --default-answer --confirm-command install - Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\$ver" - Add-Content $env:GITHUB_PATH "C:\VulkanSDK\$ver\Bin" + uses: ./.github/actions/install-vulkan-sdk-windows + with: + arch: ${{ matrix.arch }} - name: Build run: python tools/build.py windows --arch ${{ matrix.arch }} --backend ${{ matrix.backend }} - name: Validate Windows backend artifacts diff --git a/.github/workflows/validate_wrapper.yml b/.github/workflows/validate_wrapper.yml index 896249e..e7434d8 100644 --- a/.github/workflows/validate_wrapper.yml +++ b/.github/workflows/validate_wrapper.yml @@ -231,6 +231,46 @@ jobs: $env:PATH = "$wrapperDir;$upstreamDir;$env:PATH" ctest --test-dir build/wa64 -C Release --timeout 120 --verbose --output-on-failure + windows-arm64-vulkan: + needs: changes + if: needs.changes.outputs.native == 'true' + runs-on: windows-11-vs2026-arm + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + upstream: [pinned, v0.5.0] + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + submodules: recursive + - name: Setup ARM64 MSVC environment + uses: TheMrMilchmann/setup-msvc-dev@v4.1.0 + with: + arch: arm64 + - name: Select candidate upstream + if: matrix.upstream != 'pinned' + shell: bash + run: | + git -C third_party/llama.cpp fetch --depth=1 origin "$LLAMADART_V050_QUALIFICATION_SHA" + git -C third_party/llama.cpp checkout --detach "$LLAMADART_V050_QUALIFICATION_SHA" + test "$(git -C third_party/llama.cpp rev-parse HEAD)" = "$LLAMADART_V050_QUALIFICATION_SHA" + - name: Install the release Vulkan SDK + uses: ./.github/actions/install-vulkan-sdk-windows + with: + arch: arm64 + - name: Build the release Windows ARM64 Vulkan lane + run: python tools/build.py windows --arch arm64 --backend vulkan + - name: Check the backend is ARM64 and imports the Vulkan loader + run: | + $dll = "bin/windows/arm64/ggml-vulkan.dll" + if (-not (Test-Path $dll)) { throw "Missing $dll" } + $headers = (& dumpbin /headers $dll) -join "`n" + if ($headers -notmatch "\(ARM64\)") { throw "$dll is not ARM64" } + $deps = (& dumpbin /dependents $dll) -join "`n" + if ($deps -notmatch "(?im)^\s*vulkan-1\.dll\s*$") { throw "$dll does not import vulkan-1.dll" } + linux-artifact-contract: needs: changes if: needs.changes.outputs.native == 'true' @@ -380,7 +420,7 @@ jobs: wrapper-validation: if: always() - needs: [changes, android-arm64-isa, android-vulkan-shaders, kleidiai-dispatch-emulated, windows-arm64-kleidiai, linux-artifact-contract, wrapper-contract, msvc-mtmd-link-contract] + needs: [changes, android-arm64-isa, android-vulkan-shaders, kleidiai-dispatch-emulated, windows-arm64-kleidiai, windows-arm64-vulkan, linux-artifact-contract, wrapper-contract, msvc-mtmd-link-contract] runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 diff --git a/docs/platform_backend_strategy.md b/docs/platform_backend_strategy.md index bb5f0bd..8eed3eb 100644 --- a/docs/platform_backend_strategy.md +++ b/docs/platform_backend_strategy.md @@ -88,6 +88,10 @@ The Android artifact check is `tools/validate_android_cpu_isa.py --help`. VS 2022 installed, configure directly with `cmake --preset windows-arm64-full -G "Visual Studio 17 2022"` in a fresh build directory. +- Windows ARM64 Vulkan links LunarG's native ARM64 SDK (`warm` platform), whose + `vulkan-1.lib` is ARM64. The x64 SDK's import library is x64-only, which + VS 2026's ClangCL linker (`lld-link`) rejects. `.github/actions/install-vulkan-sdk-windows` + pins both installers by SHA-256 and checks the import library's machine type. - Windows MSVC builds disable IPO/LTCG for `llama-common` and `mtmd` by default. Current MSVC `link.exe` can access-violate when linking the large `llama-common` utility DLL with `/LTCG`. CMake's automatic Windows export diff --git a/tests/test_ci_scope.py b/tests/test_ci_scope.py index 5aaa9be..80c16c8 100644 --- a/tests/test_ci_scope.py +++ b/tests/test_ci_scope.py @@ -13,7 +13,7 @@ from verify_release_provenance import workflow_job JOBS = ('android-arm64-isa', 'android-vulkan-shaders', 'kleidiai-dispatch-emulated', - 'windows-arm64-kleidiai', 'linux-artifact-contract', + 'windows-arm64-kleidiai', 'windows-arm64-vulkan', 'linux-artifact-contract', 'wrapper-contract', 'msvc-mtmd-link-contract') diff --git a/tests/test_windows_vulkan_sdk.py b/tests/test_windows_vulkan_sdk.py new file mode 100644 index 0000000..a2b179b --- /dev/null +++ b/tests/test_windows_vulkan_sdk.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from pathlib import Path +import unittest + +ROOT = Path(__file__).resolve().parents[1] + + +class WindowsVulkanSdkTest(unittest.TestCase): + def test_arm64_uses_native_arm64_sdk_and_checks_import_library(self) -> None: + action = (ROOT / ".github/actions/install-vulkan-sdk-windows/action.yml").read_text(encoding="utf-8") + self.assertIn("/warm/vulkansdk-windows-ARM64-$ver.exe", action) + self.assertIn("/windows/vulkansdk-windows-X64-$ver.exe", action) + self.assertIn("29c3e7b9ff9c9d38455708bed7e744f879c770c15f3fedae55c409817c375515", action) + self.assertIn("34a921d951858274ca8e470e9d0a3b7624db41216b3908ccea9f73c8a1b7500e", action) + self.assertIn("Get-FileHash $installer -Algorithm SHA256", action) + self.assertIn('dumpbin /headers $lib', action) + + def test_release_and_validation_lanes_use_the_action(self) -> None: + release = (ROOT / ".github/workflows/native_release.yml").read_text(encoding="utf-8") + windows_job = release.split("\n build-windows:\n", 1)[1].split("\n smoke-release-contract:\n", 1)[0] + self.assertIn("uses: ./.github/actions/install-vulkan-sdk-windows\n with:\n arch: ${{ matrix.arch }}", windows_job) + self.assertNotIn("sdk.lunarg.com", release) + validate = (ROOT / ".github/workflows/validate_wrapper.yml").read_text(encoding="utf-8") + lane = validate.split("\n windows-arm64-vulkan:\n", 1)[1].split("\n linux-artifact-contract:\n", 1)[0] + self.assertIn("uses: ./.github/actions/install-vulkan-sdk-windows\n with:\n arch: arm64", lane) + self.assertIn("python tools/build.py windows --arch arm64 --backend vulkan", lane) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/ci_scope.py b/tools/ci_scope.py index 4ec9c64..72206b1 100644 --- a/tools/ci_scope.py +++ b/tools/ci_scope.py @@ -49,7 +49,7 @@ def validate_results(needs): return False expected = 'success' if selected == 'true' else 'skipped' jobs = {'android-arm64-isa', 'android-vulkan-shaders', 'kleidiai-dispatch-emulated', - 'windows-arm64-kleidiai', 'linux-artifact-contract', + 'windows-arm64-kleidiai', 'windows-arm64-vulkan', 'linux-artifact-contract', 'wrapper-contract', 'msvc-mtmd-link-contract'} return set(needs) == jobs | {'changes'} and all( needs[job].get('result') == expected for job in jobs