This is an automated email from the ASF dual-hosted git repository.
spectrometerHBH 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 0d2205f25 [CI] Split minimal example runners and cover Windows and
macOS (#115)
0d2205f25 is described below
commit 0d2205f2530843eef1e396b2680a37633ff97150
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Oct 28 15:13:28 2026 -0400
[CI] Split minimal example runners and cover Windows and macOS (#115)
Run the quick-start examples through separate argument-free CPU and CUDA
task scripts, each retaining an explicit example list. Update the
existing Jenkins unit-test calls and run the CPU examples in the
existing Windows and macOS/Metal test steps.
Use the native MSVC ABI and installed FFI import library for standard
Windows module exports. Extend the Windows CPU runner to build and run
the C++ loader with CMake, including multi-config output placement and
runtime DLL discovery. Use the runtime tensor allocator for portable
alignment, remove the redundant Linux linker workaround, and update the
adjacent example documentation.
---
.github/workflows/main.yml | 18 ++++++++-------
ci/jenkins/cpu_jenkinsfile.groovy | 2 +-
ci/jenkins/gpu_jenkinsfile.groovy | 2 +-
docs/get_started/quick_start.rst | 4 ++--
examples/quick_start/CMakeLists.txt | 5 ----
examples/quick_start/README.md | 2 +-
examples/quick_start/load_cpp.cc | 16 ++++---------
examples/quick_start/run_all_cpu.bat | 7 ++++++
python/tvm_tirx/codegen/executable.py | 5 ++++
python/tvm_tirx/support/cc.py | 3 ++-
tests/python/codegen/llvm/test_minimal.py | 27 ++++++++++++++++++++++
.../scripts/task_minimal_examples_cpu.bat | 13 ++++-------
...al_examples.sh => task_minimal_examples_cpu.sh} | 7 +-----
...l_examples.sh => task_minimal_examples_cuda.sh} | 7 +-----
14 files changed, 66 insertions(+), 52 deletions(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0c6891393..9dbd66561 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -72,10 +72,11 @@ jobs:
uv pip install --no-deps --force-reinstall dist/apache_tvm_tirx-*.whl
- name: Test
shell: bash
- run: >-
- uv run --no-sync python -m pytest -v
tests/python/codegen/llvm/test_minimal.py
- tests/python/runtime/test_validate_wheel_library.py
tests/python/script
- tests/python/ir/test_op_rust.py
+ run: |
+ uv run --no-sync python -m pytest -v
tests/python/codegen/llvm/test_minimal.py \
+ tests/python/runtime/test_validate_wheel_library.py
tests/python/script \
+ tests/python/ir/test_op_rust.py
+ ./tests/scripts/task_minimal_examples_cpu.sh
- name: Rust tests
timeout-minutes: 20
shell: bash
@@ -127,10 +128,11 @@ jobs:
)
- name: Test
shell: cmd /C call {0}
- run: >-
- uv run --no-sync python -m pytest -v
tests/python/codegen/llvm/test_minimal.py
- tests/python/runtime/test_validate_wheel_library.py
tests/python/script
- tests/python/ir/test_op_rust.py
+ run: |
+ uv run --no-sync python -m pytest -v
tests/python/codegen/llvm/test_minimal.py ^
+ tests/python/runtime/test_validate_wheel_library.py
tests/python/script ^
+ tests/python/ir/test_op_rust.py || exit /b 1
+ call tests\scripts\task_minimal_examples_cpu.bat || exit /b 1
- name: Rust tests
timeout-minutes: 20
shell: cmd /C call {0}
diff --git a/ci/jenkins/cpu_jenkinsfile.groovy
b/ci/jenkins/cpu_jenkinsfile.groovy
index 8f91c9f32..54f149fa9 100644
--- a/ci/jenkins/cpu_jenkinsfile.groovy
+++ b/ci/jenkins/cpu_jenkinsfile.groovy
@@ -57,7 +57,7 @@ stage('Test') {
script: """${docker_run} ${images.ci_cpu} bash -euo pipefail -c '
source ./tests/scripts/test_task_install_dist_wheels.sh
./tests/scripts/task_python_unittest.sh
- ./tests/scripts/task_minimal_examples.sh cpu
+ ./tests/scripts/task_minimal_examples_cpu.sh
'""",
label: 'Run Python unit tests',
)
diff --git a/ci/jenkins/gpu_jenkinsfile.groovy
b/ci/jenkins/gpu_jenkinsfile.groovy
index 9f2374259..e6d1c5789 100644
--- a/ci/jenkins/gpu_jenkinsfile.groovy
+++ b/ci/jenkins/gpu_jenkinsfile.groovy
@@ -66,7 +66,7 @@ parallel(
script: """${docker_run} ${images.ci_gpu} bash -euo pipefail -c '
source ./tests/scripts/test_task_install_dist_wheels.sh --group
cuda
./tests/scripts/task_python_unittest_gpuonly.sh
- ./tests/scripts/task_minimal_examples.sh cuda
+ ./tests/scripts/task_minimal_examples_cuda.sh
'""",
label: 'Run Python GPU unit tests',
)
diff --git a/docs/get_started/quick_start.rst b/docs/get_started/quick_start.rst
index 35f1d0b51..cd8cb41ff 100644
--- a/docs/get_started/quick_start.rst
+++ b/docs/get_started/quick_start.rst
@@ -250,8 +250,8 @@ Load from C++
The following C++ program loads the CPU build, ``add_one_cpu.so``, and calls
``add_one`` through the TVM-FFI C++ API. TIRx kernels require 64-byte aligned
-tensor data for performance, so the program allocates its tensors with
-``std::aligned_alloc``:
+tensor data for performance, so the program uses the runtime tensor allocator
+to provide the required alignment on each platform:
.. literalinclude:: ../../examples/quick_start/load_cpp.cc
:language: cpp
diff --git a/examples/quick_start/CMakeLists.txt
b/examples/quick_start/CMakeLists.txt
index d30abcec9..e7ded3280 100644
--- a/examples/quick_start/CMakeLists.txt
+++ b/examples/quick_start/CMakeLists.txt
@@ -40,10 +40,5 @@ find_package(tvm_tirx CONFIG REQUIRED)
# Load add_one_cpu.so from C++ using only the runtime libraries.
add_executable(load_cpp load_cpp.cc)
-# load_cpp never calls the TIRx runtime directly; keep it linked so that it is
-# loaded at startup and can run the exported module.
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- target_link_options(load_cpp PRIVATE "LINKER:--no-as-needed")
-endif ()
target_link_libraries(load_cpp PRIVATE tvm_runtime::shared tvm_ffi::shared)
set_target_properties(load_cpp PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}")
diff --git a/examples/quick_start/README.md b/examples/quick_start/README.md
index 0098147db..f40e9bdd2 100644
--- a/examples/quick_start/README.md
+++ b/examples/quick_start/README.md
@@ -31,7 +31,7 @@ This directory contains all the source code for the
```bash
pip install apache-tvm-tirx torch
-bash run_all_cpu.sh # on Windows: run_all_cpu.bat (Python steps only)
+bash run_all_cpu.sh # on Windows: run_all_cpu.bat
# needs an NVIDIA GPU with CUDA 13 or newer
pip install "apache-tvm-tirx[cuda]"
diff --git a/examples/quick_start/load_cpp.cc b/examples/quick_start/load_cpp.cc
index 5ce19ff8f..c187cf664 100644
--- a/examples/quick_start/load_cpp.cc
+++ b/examples/quick_start/load_cpp.cc
@@ -20,26 +20,18 @@
// [main.begin]
#include <tvm/ffi/extra/module.h>
#include <tvm/ffi/tvm_ffi.h>
+#include <tvm/runtime/tensor.h>
-#include <cstdlib>
#include <iostream>
namespace ffi = tvm::ffi;
-// TIRx kernels require 64-byte aligned tensor data for performance.
-struct CPUAllocator {
- void AllocData(DLTensor* tensor) {
- size_t bytes = (tensor->shape[0] * sizeof(float) + 63) / 64 * 64;
- tensor->data = std::aligned_alloc(64, bytes);
- }
- void FreeData(DLTensor* tensor) { std::free(tensor->data); }
-};
-
int main() {
DLDataType f32{kDLFloat, 32, 1};
DLDevice cpu{kDLCPU, 0};
- ffi::Tensor a = ffi::Tensor::FromNDAlloc(CPUAllocator(), {10}, f32, cpu);
- ffi::Tensor b = ffi::Tensor::FromNDAlloc(CPUAllocator(), {10}, f32, cpu);
+ // The runtime allocator provides the alignment required by TIRx kernels.
+ ffi::Tensor a = tvm::runtime::Empty({10}, f32, cpu);
+ ffi::Tensor b = tvm::runtime::Empty({10}, f32, cpu);
float* a_data = static_cast<float*>(a.data_ptr());
for (int i = 0; i < 10; ++i) a_data[i] = static_cast<float>(i);
diff --git a/examples/quick_start/run_all_cpu.bat
b/examples/quick_start/run_all_cpu.bat
index 11798399e..9e2c63d61 100644
--- a/examples/quick_start/run_all_cpu.bat
+++ b/examples/quick_start/run_all_cpu.bat
@@ -28,4 +28,11 @@ python load_add_one_cpu.py || exit /b 1
rem Inspect add_one and specialize it to 128 elements
python inspect_and_transform.py || exit /b 1
+rem Load add_one_cpu.so from C++ using only the runtime libraries.
+cmake . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO="%CD%\build" || exit /b 1
+cmake --build build --config RelWithDebInfo || exit /b 1
+for /f "delims=" %%d in ('python -m tvm_tirx.config --libdir') do set
"PATH=%%d;%PATH%"
+for /f "delims=" %%d in ('python -m tvm_ffi.config --libdir') do set
"PATH=%%d;%PATH%"
+build\load_cpp.exe || exit /b 1
+
endlocal
diff --git a/python/tvm_tirx/codegen/executable.py
b/python/tvm_tirx/codegen/executable.py
index ae72fd7fa..918e82777 100644
--- a/python/tvm_tirx/codegen/executable.py
+++ b/python/tvm_tirx/codegen/executable.py
@@ -18,6 +18,7 @@
import json
import os
+import sys
from collections.abc import Callable
from typing import Any
@@ -280,6 +281,10 @@ class Executable:
else:
fcompile = _cc.create_shared
+ if sys.platform == "win32" and fcompile is _cc.create_shared:
+ # Windows DLLs must resolve generated FFI calls through the import
library.
+ files.append(tvm_ffi_libinfo.find_windows_implib())
+
if llvm_target is None and hasattr(fcompile, "get_target_triple"):
triple = fcompile.get_target_triple()
assert triple, "Target triple should not be empty"
diff --git a/python/tvm_tirx/support/cc.py b/python/tvm_tirx/support/cc.py
index c3c54fe3d..35497aafc 100644
--- a/python/tvm_tirx/support/cc.py
+++ b/python/tvm_tirx/support/cc.py
@@ -375,7 +375,8 @@ def _linux_compile(
def _windows_compile(output, objects, options, cwd=None, ccache_env=None):
compiler = os.getenv("TVM_TIRX_WIN_CC", default="clang")
- win_target = os.getenv("TVM_TIRX_WIN_TARGET", default="x86_64")
+ # Match the MSVC ABI used by native Windows LLVM objects and import
libraries.
+ win_target = os.getenv("TVM_TIRX_WIN_TARGET",
default="x86_64-pc-windows-msvc")
cmd = [compiler]
cmd += ["-O2"]
cmd += ["--target=" + win_target]
diff --git a/tests/python/codegen/llvm/test_minimal.py
b/tests/python/codegen/llvm/test_minimal.py
index 2e7680d60..be5e3a26e 100644
--- a/tests/python/codegen/llvm/test_minimal.py
+++ b/tests/python/codegen/llvm/test_minimal.py
@@ -22,6 +22,7 @@ import pytest
import tvm_tirx
import tvm_tirx.testing
from tvm_tirx.script import tirx as T
+from tvm_tirx.support import cc
from tvm_tirx.testing import env
@@ -57,5 +58,31 @@ def test_llvm_add_pipeline():
tvm_tirx.testing.assert_allclose(c.numpy(), a.numpy() + b.numpy())
[email protected](not env.has_llvm(), reason="need llvm")
[email protected]("fcompile", [None, cc.create_shared])
+def test_llvm_export_and_reload(tmp_path, fcompile):
+ n = T.dynamic("n")
+
+ @T.function
+ def add_one(A: T.Tensor((n,), "float32"), B: T.Tensor((n,), "float32")):
+ for i in range(n):
+ B[i] = A[i] + 1
+
+ kernel = tvm_tirx.compile(add_one, target="llvm")
+ path = tmp_path / "add_one.so"
+ kernel.export_library(path, fcompile=fcompile)
+ loaded = tvm_tirx.runtime.load_module(str(path))["add_one"]
+ dev = tvm_tirx.cpu(0)
+ a = tvm_tirx.runtime.tensor(np.arange(10, dtype="float32"), dev)
+ b = tvm_tirx.runtime.tensor(np.zeros(10, dtype="float32"), dev)
+ loaded(a, b)
+ tvm_tirx.testing.assert_allclose(b.numpy(), a.numpy() + 1)
+
+ # Exercise the generated error path that calls the FFI library directly.
+ wrong_shape = tvm_tirx.runtime.tensor(np.zeros(11, dtype="float32"), dev)
+ with pytest.raises(ValueError, match="shape"):
+ loaded(a, wrong_shape)
+
+
if __name__ == "__main__":
tvm_tirx.testing.main()
diff --git a/examples/quick_start/run_all_cpu.bat
b/tests/scripts/task_minimal_examples_cpu.bat
similarity index 73%
copy from examples/quick_start/run_all_cpu.bat
copy to tests/scripts/task_minimal_examples_cpu.bat
index 11798399e..1610a220b 100644
--- a/examples/quick_start/run_all_cpu.bat
+++ b/tests/scripts/task_minimal_examples_cpu.bat
@@ -17,15 +17,10 @@ rem specific language governing permissions and limitations
rem under the License.
setlocal
-cd /d "%~dp0"
-rem Write, compile, and run add_one; export it to add_one_cpu.so
-python add_one_cpu.py || exit /b 1
-
-rem Load add_one_cpu.so in a new Python process
-python load_add_one_cpu.py || exit /b 1
-
-rem Inspect add_one and specialize it to 128 elements
-python inspect_and_transform.py || exit /b 1
+rem Keep this explicit list limited to examples that run in a few seconds.
+for %%e in (examples\quick_start) do (
+ uv run --no-sync cmd /c call "%%e\run_all_cpu.bat" || exit /b 1
+)
endlocal
diff --git a/tests/scripts/task_minimal_examples.sh
b/tests/scripts/task_minimal_examples_cpu.sh
similarity index 85%
copy from tests/scripts/task_minimal_examples.sh
copy to tests/scripts/task_minimal_examples_cpu.sh
index 481d4c774..42c7fa376 100755
--- a/tests/scripts/task_minimal_examples.sh
+++ b/tests/scripts/task_minimal_examples_cpu.sh
@@ -18,13 +18,8 @@
set -euxo pipefail
-if [[ $# -ne 1 || ( "$1" != cpu && "$1" != cuda ) ]]; then
- echo "Usage: $0 cpu|cuda" >&2
- exit 1
-fi
-
# Keep this explicit list limited to examples that run in a few seconds.
examples=(examples/quick_start)
for example in "${examples[@]}"; do
- uv run --no-sync bash "${example}/run_all_$1.sh"
+ uv run --no-sync bash "${example}/run_all_cpu.sh"
done
diff --git a/tests/scripts/task_minimal_examples.sh
b/tests/scripts/task_minimal_examples_cuda.sh
similarity index 85%
rename from tests/scripts/task_minimal_examples.sh
rename to tests/scripts/task_minimal_examples_cuda.sh
index 481d4c774..0db34cce1 100755
--- a/tests/scripts/task_minimal_examples.sh
+++ b/tests/scripts/task_minimal_examples_cuda.sh
@@ -18,13 +18,8 @@
set -euxo pipefail
-if [[ $# -ne 1 || ( "$1" != cpu && "$1" != cuda ) ]]; then
- echo "Usage: $0 cpu|cuda" >&2
- exit 1
-fi
-
# Keep this explicit list limited to examples that run in a few seconds.
examples=(examples/quick_start)
for example in "${examples[@]}"; do
- uv run --no-sync bash "${example}/run_all_$1.sh"
+ uv run --no-sync bash "${example}/run_all_cuda.sh"
done