Skip to content
Draft
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
32 changes: 27 additions & 5 deletions .github/workflows/native_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ jobs:
backend_glob: "*ggml-blas*.dll"
- arch: arm64
vcpkg_triplet: arm64-windows
runs_on: windows-11-arm
runs_on: windows-2022
backend: vulkan
include_core: true
needs_cuda: false
Expand Down Expand Up @@ -791,7 +791,12 @@ jobs:
with:
arch: x64
- name: Setup MSVC (arm64 cross)
if: ${{ matrix.arch == 'arm64' }}
if: ${{ matrix.arch == 'arm64' && matrix.backend == 'vulkan' }}
uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64_arm64
- name: Setup MSVC (arm64 native)
if: ${{ matrix.arch == 'arm64' && matrix.backend != 'vulkan' }}
uses: ilammy/msvc-dev-cmd@v1
with:
arch: arm64
Expand All @@ -802,8 +807,8 @@ jobs:
expected_commit: ${{ needs.resolve-tag.outputs.llama_cpp_commit }}
github_token: ${{ github.token }}
- name: Install Ninja
if: ${{ matrix.arch == 'x64' }}
run: choco install ninja -y
if: ${{ matrix.arch == 'x64' || matrix.backend == 'vulkan' }}
run: choco install ninja -y --no-progress
- name: Install CUDA Toolkit
if: ${{ matrix.needs_cuda }}
run: |
Expand Down Expand Up @@ -872,7 +877,7 @@ jobs:
if: ${{ matrix.arch == 'x64' }}
run: |
echo "SCCACHE_GHA_ENABLED=true" >> $env:GITHUB_ENV
- name: Disable sccache for arm64 Visual Studio builds
- name: Disable sccache for arm64 builds
if: ${{ matrix.arch == 'arm64' }}
run: |
Add-Content $env:GITHUB_ENV "CMAKE_C_COMPILER_LAUNCHER="
Expand Down Expand Up @@ -948,6 +953,23 @@ jobs:
if (-not (Select-String -Path $cache -Pattern '^GGML_CPU_KLEIDIAI:BOOL=ON$' -Quiet)) {
throw "Kleidi is not enabled in windows arm64 build"
}
$artifacts = @(Get-ChildItem $out -Filter '*.dll')
foreach ($artifact in $artifacts) {
$headers = dumpbin /headers $artifact.FullName | Out-String
if ($LASTEXITCODE -ne 0 -or $headers -notmatch '(?im)^\s*AA64 machine \(ARM64\)\s*$') {
throw "Expected ARM64 PE machine for $($artifact.Name)"
}
}
if ("${{ matrix.backend }}" -eq "vulkan") {
if (-not (Select-String -Path $cache -Pattern '^LLAMADART_WINDOWS_ARM64_VULKAN_FAST_COMPILE:BOOL=ON$' -Quiet)) {
throw "Windows arm64 Vulkan fast compile mode is not enabled"
}
$vulkan = Get-ChildItem $out -Filter '*ggml-vulkan*.dll' | Select-Object -First 1
$dependencies = dumpbin /dependents $vulkan.FullName | Out-String
if ($LASTEXITCODE -ne 0 -or $dependencies -notmatch '(?im)^\s*ggml-base\.dll\s*$') {
throw "Windows arm64 Vulkan backend does not depend on ggml-base.dll as expected"
}
}
}
- name: Stage upload payload
run: |
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/validate_wrapper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ on:
paths:
- '.github/workflows/validate_wrapper.yml'
- 'CMakeLists.txt'
- 'CMakePresets.json'
- 'cmake/windows-arm64-clang-toolchain.cmake'
- 'src/**'
- 'tests/**'
- 'tools/build.py'
- 'tools/tests/**'
- 'tools/tts_smoke.cpp'
- 'tools/validate_exports.py'
push:
Expand All @@ -15,8 +19,12 @@ on:
paths:
- '.github/workflows/validate_wrapper.yml'
- 'CMakeLists.txt'
- 'CMakePresets.json'
- 'cmake/windows-arm64-clang-toolchain.cmake'
- 'src/**'
- 'tests/**'
- 'tools/build.py'
- 'tools/tests/**'
- 'tools/tts_smoke.cpp'
- 'tools/validate_exports.py'

Expand All @@ -30,6 +38,8 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Validate build configuration contracts
run: python3 -m unittest discover -s tools/tests -v
- name: Configure wrapper contract tests
run: >-
cmake -S . -B build/wrapper-contract -G Ninja
Expand Down
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ set(LLAMADART_CPU_REQUIRED_FEATURES "" CACHE STRING
"Semicolon-separated GGML_USE_* defines for Android arm64 CPU backend scoring")
set(LLAMADART_MSVC_LLAMA_COMMON_IPO OFF CACHE BOOL
"Enable MSVC IPO/LTCG for llama-common")
set(LLAMADART_WINDOWS_ARM64_VULKAN_FAST_COMPILE OFF CACHE BOOL
"Reduce host optimization for faster Windows ARM64 Vulkan builds")

function(llamadart_attach_android_cpu_backend_score target_name)
if (NOT ANDROID OR NOT TARGET ${target_name} OR NOT GGML_CPU_ARM_ARCH)
Expand Down Expand Up @@ -125,6 +127,15 @@ if (TARGET ggml-vulkan AND LLAMADART_SPIRV_HEADERS_INCLUDE)
target_include_directories(ggml-vulkan SYSTEM PRIVATE "${LLAMADART_SPIRV_HEADERS_INCLUDE}")
endif()

if (WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$" AND
TARGET ggml-vulkan AND LLAMADART_WINDOWS_ARM64_VULKAN_FAST_COMPILE AND
CMAKE_CXX_COMPILER_ID MATCHES "^(Clang|GNU)$")
# ggml-vulkan.cpp is a very large host-side translation unit. Clang's -O3
# compile dominates this cross-build; -Os does not alter embedded SPIR-V
# and leaves the Release IPO setting intact for final size optimization.
target_compile_options(ggml-vulkan PRIVATE -Os)
endif()

if (MSVC AND NOT LLAMADART_MSVC_LLAMA_COMMON_IPO)
# MSVC link.exe can access-violate while linking llama-common.dll with
# LTCG. Keep IPO elsewhere, but compile this utility stack without LTCG.
Expand Down
27 changes: 27 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,32 @@
"GGML_CPU_KLEIDIAI": "ON"
}
},
{
"name": "windows-arm64-vulkan-fast",
"inherits": "windows-base",
"binaryDir": "${sourceDir}/build/wa64",
"generator": "Ninja",
"architecture": {
"value": "arm64",
"strategy": "external"
},
"toolset": {
"value": "host=x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/windows-arm64-clang-toolchain.cmake",
"CMAKE_CXX_FLAGS": "-fexceptions -DWIN32_LEAN_AND_MEAN -DNOMINMAX",
"LLAMADART_WINDOWS_ARM64_VULKAN_FAST_COMPILE": "ON",
"GGML_BLAS": "ON",
"GGML_BLAS_VENDOR": "OpenBLAS",
"GGML_VULKAN": "ON",
"GGML_VULKAN_SHADERS_GEN_TOOLCHAIN": "${sourceDir}/cmake/windows-host-x64-toolchain.cmake",
"GGML_OPENCL": "OFF",
"GGML_CUDA": "OFF",
"GGML_CPU_KLEIDIAI": "ON"
}
},

{
"name": "android-arm64-v8a-full",
Expand Down Expand Up @@ -228,6 +254,7 @@
{ "name": "linux-arm64-full", "configurePreset": "linux-arm64-full" },
{ "name": "windows-x64-full", "configurePreset": "windows-x64-full" },
{ "name": "windows-arm64-full", "configurePreset": "windows-arm64-full", "configuration": "Release" },
{ "name": "windows-arm64-vulkan-fast", "configurePreset": "windows-arm64-vulkan-fast" },
{ "name": "android-arm64-v8a-full", "configurePreset": "android-arm64-v8a-full" },
{ "name": "android-x86_64-full", "configurePreset": "android-x86_64-full" }
]
Expand Down
36 changes: 36 additions & 0 deletions cmake/windows-arm64-clang-toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR ARM64)

# Use the x64-hosted LLVM compiler from Visual Studio to produce generic
# Windows ARM64 binaries. CPU-specific architecture flags belong in runtime
# dispatched variants, not in this baseline package.
set(_llamadart_arm64_target arm64-pc-windows-msvc)

if(DEFINED ENV{VSINSTALLDIR})
file(TO_CMAKE_PATH "$ENV{VSINSTALLDIR}" _llamadart_vs_install_dir)
set(_llamadart_llvm_bin "${_llamadart_vs_install_dir}/VC/Tools/Llvm/x64/bin")
find_program(
_llamadart_clang
NAMES clang.exe clang
PATHS "${_llamadart_llvm_bin}"
NO_DEFAULT_PATH
)
find_program(
_llamadart_clangxx
NAMES clang++.exe clang++
PATHS "${_llamadart_llvm_bin}"
NO_DEFAULT_PATH
)
endif()

if(NOT _llamadart_clang)
find_program(_llamadart_clang NAMES clang.exe clang REQUIRED)
endif()
if(NOT _llamadart_clangxx)
find_program(_llamadart_clangxx NAMES clang++.exe clang++ REQUIRED)
endif()

set(CMAKE_C_COMPILER "${_llamadart_clang}")
set(CMAKE_CXX_COMPILER "${_llamadart_clangxx}")
set(CMAKE_C_COMPILER_TARGET "${_llamadart_arm64_target}")
set(CMAKE_CXX_COMPILER_TARGET "${_llamadart_arm64_target}")
8 changes: 7 additions & 1 deletion tools/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
}


def windows_preset(arch: str, backend: str) -> str:
if arch == "arm64" and backend == "vulkan":
return "windows-arm64-vulkan-fast"
return f"windows-{arch}-full"


def fail(message: str) -> NoReturn:
raise SystemExit(message)

Expand Down Expand Up @@ -1114,7 +1120,7 @@ def build_windows(args: argparse.Namespace) -> None:
if cache_vars["GGML_CUDA"] == "ON" and not (shutil.which("nvcc") or shutil.which("nvcc.exe")):
fail("Windows CUDA backend build requires CUDA (nvcc not found in PATH)")

preset = f"windows-{arch}-full"
preset = windows_preset(arch, backend)
clean_build_dir(preset, args.clean)

extra_args = cmake_cache_args(cache_vars)
Expand Down
55 changes: 55 additions & 0 deletions tools/tests/test_windows_build_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import json
import unittest
from pathlib import Path

from tools import build


ROOT = Path(__file__).resolve().parents[2]


class WindowsBuildConfigTest(unittest.TestCase):
def test_arm64_vulkan_uses_fast_cross_preset(self) -> None:
self.assertEqual(
build.windows_preset("arm64", "vulkan"),
"windows-arm64-vulkan-fast",
)

def test_other_windows_builds_keep_full_presets(self) -> None:
for arch, backend in (
("arm64", "blas"),
("x64", "vulkan"),
("x64", "cuda"),
):
with self.subTest(arch=arch, backend=backend):
self.assertEqual(
build.windows_preset(arch, backend),
f"windows-{arch}-full",
)

def test_fast_preset_keeps_arm64_release_contract(self) -> None:
document = json.loads((ROOT / "CMakePresets.json").read_text())
preset = next(
preset
for preset in document["configurePresets"]
if preset["name"] == "windows-arm64-vulkan-fast"
)

self.assertEqual(preset["generator"], "Ninja")
self.assertEqual(preset["architecture"]["value"], "arm64")
self.assertEqual(
preset["cacheVariables"]["CMAKE_TOOLCHAIN_FILE"],
"${sourceDir}/cmake/windows-arm64-clang-toolchain.cmake",
)
self.assertEqual(
preset["cacheVariables"][
"LLAMADART_WINDOWS_ARM64_VULKAN_FAST_COMPILE"
],
"ON",
)
self.assertEqual(preset["cacheVariables"]["GGML_VULKAN"], "ON")
self.assertEqual(preset["cacheVariables"]["GGML_CPU_KLEIDIAI"], "ON")


if __name__ == "__main__":
unittest.main()
Loading