This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-tirx.git
The following commit(s) were added to refs/heads/main by this push:
new a04dab5b3 [CI] Enable native Windows ARM64 wheels (#85)
a04dab5b3 is described below
commit a04dab5b3a7d339f027c12ae9d713c566f280dc9
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Oct 26 12:27:51 2026 -0400
[CI] Enable native Windows ARM64 wheels (#85)
Add native Windows ARM64 entries for the TIRx core wheel, optional LLVM
targets wheel, and CUDA runtime sidecar.
Extend the existing Windows sidecar build to select
architecture-specific CUDA and MSVC tools, resolve CUDA import libraries
for the compiler target, and repair ARM64 wheels with the matching MSVC
runtime. Keep the existing platform matrices and publication gating.
Initialize optional LLVM wheel loader hooks before loading its backend
so normal core-first imports resolve repaired Windows DLL dependencies.
---
.github/actions/build-wheel-for-publish/action.yml | 19 ++++++-
.github/workflows/publish_wheel.yml | 17 +++++-
ci/wheel/check_llvm_targets_wheel.py | 5 +-
ci/wheel/windows_build_libtvm_runtime_cuda.bat | 63 +++++++++++++++-------
cmake/utils/FindCUDA.cmake | 32 +++++++----
python/tvm_tirx/base.py | 3 ++
6 files changed, 106 insertions(+), 33 deletions(-)
diff --git a/.github/actions/build-wheel-for-publish/action.yml
b/.github/actions/build-wheel-for-publish/action.yml
index 93dbd0272..dd218fa41 100644
--- a/.github/actions/build-wheel-for-publish/action.yml
+++ b/.github/actions/build-wheel-for-publish/action.yml
@@ -22,7 +22,7 @@ description: >
inputs:
arch:
- description: "Target architecture for cibuildwheel (e.g., x86_64, aarch64,
arm64, AMD64)"
+ description: "Target architecture for cibuildwheel (e.g., x86_64, aarch64,
arm64, AMD64, ARM64)"
required: true
build:
description: "cibuildwheel build selector (e.g., cp310-manylinux_x86_64)"
@@ -32,7 +32,7 @@ inputs:
required: false
default: ""
include_cuda_runtime:
- description: "Set to 1 to build and inject the CUDA runtime library (Linux
only)"
+ description: "Set to 1 to build and inject the CUDA runtime library (Linux
and Windows)"
required: false
default: "0"
@@ -59,6 +59,21 @@ runs:
shell: bash
run: echo "TVM_CUDA_EXTRA_LIB=$(cygpath -m
"$(pwd)")/build-wheel-cuda/lib/tvm_runtime_cuda.dll" >> "$GITHUB_ENV"
+ # Prefer the redistributable matching the native compiler over older DLLs
+ # shipped alongside the runner's tools when delvewheel follows PATH.
+ - name: Select native MSVC runtime DLLs (Windows ARM64)
+ if: runner.os == 'Windows' && inputs.arch == 'ARM64'
+ shell: pwsh
+ run: |
+ $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual
Studio\Installer\vswhere.exe"
+ $vs = & $vswhere -latest -products * -requires
Microsoft.VisualStudio.Component.VC.Tools.ARM64 -property installationPath
+ if (-not $vs) { throw "Visual Studio ARM64 tools not found" }
+ $version = (Get-Content
"$vs\VC\Auxiliary\Build\Microsoft.VCRedistVersion.default.txt").Trim()
+ $crt = Get-ChildItem "$vs\VC\Redist\MSVC\$version\arm64" -Directory
-Filter 'Microsoft.VC*.CRT' | Select-Object -First 1
+ if (-not $crt -or -not (Test-Path "$($crt.FullName)\msvcp140.dll")) {
throw "Native MSVC redistributable not found" }
+ $crt.FullName | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8
-Append
+ Write-Host "Wheel repair will use $($crt.FullName)"
+
# ---- Build and test wheels ----
- name: Build and test TIRx core wheel
uses: pypa/cibuildwheel@b3395e271a35f73dad77f21802abe9b986dfe299 #
v4.3.1
diff --git a/.github/workflows/publish_wheel.yml
b/.github/workflows/publish_wheel.yml
index 85c504105..cea3130b4 100644
--- a/.github/workflows/publish_wheel.yml
+++ b/.github/workflows/publish_wheel.yml
@@ -74,6 +74,12 @@ jobs:
arch: AMD64
script: ci/wheel/windows_build_libtvm_runtime_cuda.bat
lib: build-wheel-cuda/lib/tvm_runtime_cuda.dll
+ - name: "CUDA runtime sidecar (Windows ARM64)"
+ os: windows-11-arm
+ container: ""
+ arch: ARM64
+ script: ci/wheel/windows_build_libtvm_runtime_cuda.bat
+ lib: build-wheel-cuda/lib/tvm_runtime_cuda.dll
steps:
# The containerized Linux legs check out as root; mark the tree safe so
git
# (submodule init in checkout) does not bail on "dubious ownership".
@@ -95,7 +101,8 @@ jobs:
if: runner.os == 'Windows'
uses: actions/[email protected]
with:
- python-version: "3.10"
+ python-version: ${{ matrix.arch == 'ARM64' && '3.12' || '3.10' }}
+ architecture: ${{ matrix.arch == 'ARM64' && 'arm64' || 'x64' }}
- name: Build CUDA runtime sidecar (Unix)
if: runner.os != 'Windows'
@@ -105,7 +112,7 @@ jobs:
- name: Build CUDA runtime sidecar (Windows)
if: runner.os == 'Windows'
shell: cmd
- run: call ${{ matrix.script }}
+ run: call ${{ matrix.script }} ${{ matrix.arch }}
- name: Upload CUDA runtime sidecar
uses: actions/[email protected]
@@ -149,6 +156,12 @@ jobs:
build: cp310-win_amd64
include_cuda_runtime: "1"
artifact_suffix: windows-amd64
+ - name: "Windows ARM64 wheel with CUDA runtime"
+ os: windows-11-arm
+ arch: ARM64
+ build: cp312-win_arm64
+ include_cuda_runtime: "1"
+ artifact_suffix: windows-arm64
steps:
- name: Checkout source
uses: actions/[email protected]
diff --git a/ci/wheel/check_llvm_targets_wheel.py
b/ci/wheel/check_llvm_targets_wheel.py
index ef5f12051..6ea8aff91 100644
--- a/ci/wheel/check_llvm_targets_wheel.py
+++ b/ci/wheel/check_llvm_targets_wheel.py
@@ -45,13 +45,16 @@ def main():
if any(part in Path(name).name for part in forbidden):
raise RuntimeError(f"Optional wheel bundles an
FFI/native-backend library: {name}")
+ # A normal core import must initialize the optional wheel's DLL loader
hooks.
+ from importlib import import_module
from importlib.metadata import version
import tvm_ffi
- from tvm_tirx_llvm_targets import get_lib_path
from tvm_tirx import base
+ get_lib_path = import_module("tvm_tirx_llvm_targets").get_lib_path
+
assert version("apache-tvm-tirx") ==
version("apache-tvm-tirx-llvm-targets")
loaded = Path(base._LOADED_LIBS["tvm_tirx_llvm_backend"]._name).resolve()
assert loaded == get_lib_path().resolve(), (loaded, get_lib_path())
diff --git a/ci/wheel/windows_build_libtvm_runtime_cuda.bat
b/ci/wheel/windows_build_libtvm_runtime_cuda.bat
index 061a5518a..c7b189c27 100644
--- a/ci/wheel/windows_build_libtvm_runtime_cuda.bat
+++ b/ci/wheel/windows_build_libtvm_runtime_cuda.bat
@@ -28,29 +28,53 @@ pushd "%~dp0..\.." || exit /b 1
set "repo_root=%CD%"
popd
set "build_dir=%repo_root%\build-wheel-cuda"
-set "cuda_prefix=C:\opt\cuda"
+set "target_arch=%~1"
+if not defined target_arch set "target_arch=AMD64"
+set "cuda_subdir=win-64"
+set "cuda_channels=-c nvidia/label/cuda-13.0.2"
+set "cuda_package=cuda-toolkit=13.0.2"
+set "vc_component=Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
+set "vc_target=amd64"
+if /i "%target_arch%"=="ARM64" (
+ set "cuda_subdir=win-arm64"
+ set "cuda_channels=--override-channels -c
https://repo.anaconda.com/pkgs/main"
+ set "cuda_package=cuda-toolkit=13.4.3"
+ set "vc_component=Microsoft.VisualStudio.Component.VC.Tools.ARM64"
+ set "vc_target=arm64"
+) else if /i not "%target_arch%"=="AMD64" (
+ echo Unsupported target architecture: %target_arch%
+ exit /b 1
+)
+set "cuda_prefix=C:\opt\cuda-%target_arch%"
-rem Locate conda: the runner ships Miniconda (exposed via %CONDA%) but it may
not be
-rem on PATH in this shell.
+rem ARM64 runners do not ship conda. Bootstrap a pinned native installer when
+rem needed; this does not replace the Python selected by actions/setup-python.
set "conda_exe=conda"
-where conda >nul 2>nul || set "conda_exe=%CONDA%\Scripts\conda.exe"
+where conda >nul 2>nul
+if errorlevel 1 (
+ if exist "%CONDA%\Scripts\conda.exe" (
+ set "conda_exe=%CONDA%\Scripts\conda.exe"
+ ) else (
+ if /i not "%target_arch%"=="ARM64" ( echo conda not found & exit /b 1 )
+ powershell -NoProfile -ExecutionPolicy Bypass -Command
"$ErrorActionPreference='Stop'; $p=Join-Path $env:TEMP 'miniconda-arm64.exe';
Invoke-WebRequest
'https://repo.anaconda.com/miniconda/Miniconda3-py313_26.9.4-0-Windows-arm64.exe'
-OutFile $p; if ((Get-FileHash $p -Algorithm SHA256).Hash -ne
'3101d128cd1bbedf59822a0aeaae6b2c8ba3c574711a7c544bccd382a9eff307') { throw
'Miniconda checksum mismatch' }; $proc=Start-Process $p -ArgumentList
'/S','/InstallationType=JustMe','/RegisterPytho [...]
+ set "conda_exe=C:\opt\miniconda-arm64\Scripts\conda.exe"
+ )
+)
-rem Install the pinned CUDA toolkit via conda from the nvidia channel.
-rem The win-64 channel caps at 13.0.x, so this
-rem pins 13.0.2 -- slightly behind the Linux image's CUDA 13.1, which is
harmless: the
-rem sidecar has no device code and links the CUDA runtime by soname only. The
nvidia
-rem CDN occasionally returns a transient HTTP 5xx, so retry once; a
half-finished first
-rem attempt can leave the prefix partially populated, so wipe it before
retrying.
+rem Keep the existing AMD64 NVIDIA package pin. ARM64 packages are supplied by
+rem Anaconda main; pin the toolkit and target subdir instead of solving for
x64.
+set "CONDA_PLUGINS_AUTO_ACCEPT_TOS=yes"
if not exist "%cuda_prefix%\Library\bin\nvcc.exe" (
- call "%conda_exe%" create -q -p "%cuda_prefix%" -c nvidia/label/cuda-13.0.2
cuda-toolkit -y
+ call "%conda_exe%" create -q -p "%cuda_prefix%" --platform "%cuda_subdir%"
%cuda_channels% "%cuda_package%" -y
if errorlevel 1 (
if exist "%cuda_prefix%" rmdir /s /q "%cuda_prefix%"
- call "%conda_exe%" create -q -p "%cuda_prefix%" -c
nvidia/label/cuda-13.0.2 cuda-toolkit -y || exit /b 1
+ call "%conda_exe%" create -q -p "%cuda_prefix%" --platform "%cuda_subdir%"
%cuda_channels% "%cuda_package%" -y || exit /b 1
)
)
+call "%conda_exe%" list -p "%cuda_prefix%" --explicit || exit /b 1
rem conda lays the Windows toolkit out under <prefix>\Library (bin\nvcc.exe,
-rem lib\x64\cudart.lib, include\...). Discover the root from nvcc.exe so TVM's
+rem lib\<arch>\cudart.lib, include\...). Discover the root from nvcc.exe so
TVM's
rem FindCUDA MSVC branch resolves against the real layout instead of a
hardcode.
set "nvcc_exe="
for /f "delims=" %%i in ('dir /s /b "%cuda_prefix%\nvcc.exe" 2^>nul') do if
not defined nvcc_exe set "nvcc_exe=%%i"
@@ -62,16 +86,16 @@ set "cuda_root=%CD%"
popd
set "CUDA_PATH=%cuda_root%"
-python -m pip install -U pip cmake ninja "apache-tvm-ffi>=0.1.15.post0" ||
exit /b 1
+python -m pip install -U pip cmake ninja "apache-tvm-ffi>=0.1.15.post1" ||
exit /b 1
"%nvcc_exe%" --version || exit /b 1
rem nvcc needs the MSVC host compiler (cl.exe), so locate VS via vswhere and
run the
-rem cmake configure+build inside vcvars64 (this shell is not a VS Developer
prompt).
+rem cmake configure+build inside vcvarsall (this shell is not a VS Developer
prompt).
set "vswhere=C:\Program Files (x86)\Microsoft Visual
Studio\Installer\vswhere.exe"
set "vs_path="
-for /f "usebackq delims=" %%i in (`"%vswhere%" -latest -products * -requires
Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`)
do set "vs_path=%%i"
+for /f "usebackq delims=" %%i in (`"%vswhere%" -latest -products * -requires
%vc_component% -property installationPath`) do set "vs_path=%%i"
if not defined vs_path ( echo Visual Studio with VC tools not found & exit /b
1 )
-set "vcvars=%vs_path%\VC\Auxiliary\Build\vcvars64.bat"
+set "vcvars=%vs_path%\VC\Auxiliary\Build\vcvarsall.bat"
if exist "%build_dir%" rmdir /s /q "%build_dir%"
@@ -89,11 +113,14 @@ set "build_dir_fwd=%build_dir:\=/%"
set "cuda_root_fwd=%cuda_root:\=/%"
set "nvcc_fwd=%nvcc_exe:\=/%"
-call "%vcvars%" || exit /b 1
+call "%vcvars%" %vc_target% || exit /b 1
cmake -S "%repo_root_fwd%" -B "%build_dir_fwd%" -G Ninja
-DCMAKE_BUILD_TYPE=Release -DTVM_TIRX_BUILD_TESTS=OFF
-DTVM_TIRX_BUILD_PYTHON_MODULE=ON -DTVM_TIRX_USE_CUDA="%cuda_root_fwd%"
-DTVM_TIRX_USE_LLVM=OFF -DCMAKE_CUDA_COMPILER="%nvcc_fwd%"
-DCMAKE_CUDA_FLAGS="-allow-unsupported-compiler" || exit /b 1
cmake --build "%build_dir_fwd%" --target tvm_runtime tvm_runtime_cuda --config
Release || exit /b 1
if not exist "%build_dir%\lib\tvm_runtime_cuda.dll" ( echo
tvm_runtime_cuda.dll was not produced & exit /b 1 )
+dumpbin /headers "%build_dir%\lib\tvm_runtime_cuda.dll" || exit /b 1
+dumpbin /dependents "%build_dir%\lib\tvm_runtime_cuda.dll" || exit /b 1
+python -c "import pathlib,struct;
p=pathlib.Path(r'%build_dir%\lib\tvm_runtime_cuda.dll').read_bytes();
off=struct.unpack_from('<I',p,0x3c)[0];
machine=struct.unpack_from('<H',p,off+4)[0]; expected=0xaa64 if
'%target_arch%'.upper()=='ARM64' else 0x8664; assert machine==expected,
(hex(machine),hex(expected))" || exit /b 1
rem No patchelf/rpath step on Windows; delvewheel vendors dependencies at
repair time.
echo CUDA runtime: %build_dir%\lib\tvm_runtime_cuda.dll
endlocal
diff --git a/cmake/utils/FindCUDA.cmake b/cmake/utils/FindCUDA.cmake
index d6732fc2c..15ab6aab7 100644
--- a/cmake/utils/FindCUDA.cmake
+++ b/cmake/utils/FindCUDA.cmake
@@ -38,6 +38,23 @@
# - CUDA_CUBLAS_LIBRARY
#
macro(find_cuda use_cuda use_cudnn)
+ if(MSVC)
+ # Select import libraries for the compiler target, never the host
architecture.
+ if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID)
+ set(_CUDA_MSVC_TARGET_ARCH "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}")
+ elseif(CMAKE_GENERATOR_PLATFORM)
+ set(_CUDA_MSVC_TARGET_ARCH "${CMAKE_GENERATOR_PLATFORM}")
+ else()
+ set(_CUDA_MSVC_TARGET_ARCH "${CMAKE_SYSTEM_PROCESSOR}")
+ endif()
+ if(_CUDA_MSVC_TARGET_ARCH MATCHES "^(ARM64|arm64|aarch64)$")
+ set(_CUDA_MSVC_LIB_SUFFIX arm64)
+ elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(_CUDA_MSVC_LIB_SUFFIX x64)
+ else()
+ set(_CUDA_MSVC_LIB_SUFFIX Win32)
+ endif()
+ endif()
set(__use_cuda ${use_cuda})
if(${__use_cuda} MATCHES ${IS_TRUE_PATTERN})
find_package(CUDA QUIET)
@@ -48,8 +65,7 @@ macro(find_cuda use_cuda use_cudnn)
set(CUDA_FOUND TRUE)
if(MSVC)
find_library(CUDA_CUDART_LIBRARY cudart
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/Win32)
+ ${CUDA_TOOLKIT_ROOT_DIR}/lib/${_CUDA_MSVC_LIB_SUFFIX})
else(MSVC)
find_library(CUDA_CUDART_LIBRARY cudart
${CUDA_TOOLKIT_ROOT_DIR}/lib64
@@ -61,14 +77,11 @@ macro(find_cuda use_cuda use_cudnn)
if(CUDA_FOUND)
if(MSVC)
find_library(CUDA_CUDA_LIBRARY cuda
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/Win32)
+ ${CUDA_TOOLKIT_ROOT_DIR}/lib/${_CUDA_MSVC_LIB_SUFFIX})
find_library(CUDA_CUBLAS_LIBRARY cublas
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/Win32)
+ ${CUDA_TOOLKIT_ROOT_DIR}/lib/${_CUDA_MSVC_LIB_SUFFIX})
find_library(CUDA_CUBLASLT_LIBRARY cublaslt
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/Win32)
+ ${CUDA_TOOLKIT_ROOT_DIR}/lib/${_CUDA_MSVC_LIB_SUFFIX})
else(MSVC)
find_library(_CUDA_CUDA_LIBRARY cuda
PATHS ${CUDA_TOOLKIT_ROOT_DIR}
@@ -109,8 +122,7 @@ macro(find_cuda use_cuda use_cudnn)
set(CUDA_CUDNN_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
if(MSVC)
find_library(CUDA_CUDNN_LIBRARY cudnn
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
- ${CUDA_TOOLKIT_ROOT_DIR}/lib/Win32)
+ ${CUDA_TOOLKIT_ROOT_DIR}/lib/${_CUDA_MSVC_LIB_SUFFIX})
else(MSVC)
find_library(CUDA_CUDNN_LIBRARY cudnn
PATHS ${CUDA_TOOLKIT_ROOT_DIR}
diff --git a/python/tvm_tirx/base.py b/python/tvm_tirx/base.py
index 8d0370187..0110d676b 100644
--- a/python/tvm_tirx/base.py
+++ b/python/tvm_tirx/base.py
@@ -18,6 +18,7 @@
# pylint: disable=invalid-name, import-outside-toplevel
"""Base library for TVM."""
+import importlib
import importlib.util
import os
from pathlib import Path
@@ -90,6 +91,8 @@ def _load_llvm_backend(lib_paths):
except RuntimeError:
return None
if importlib.util.find_spec("tvm_tirx_llvm_targets") is not None:
+ # Initialize wheel repair hooks before loading their dependent
DLLs.
+ importlib.import_module("tvm_tirx_llvm_targets")
package = "apache-tvm-tirx-llvm-targets"
target_name = "tvm_tirx_llvm_backend"
tvm_ffi_libinfo._find_library_by_basename(