Skip to content
Open
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
2 changes: 1 addition & 1 deletion .ci/docker/ci_commit_pins/pytorch.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release/2.12
release/2.13
18 changes: 17 additions & 1 deletion .ci/docker/common/install_pytorch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ install_domains() {
pip_install --no-build-isolation --user "git+https://github.com/pytorch/vision.git@${TORCHVISION_VERSION}"
}

configure_pytorch_compiler() {
local cxx_version
cxx_version=$(/usr/bin/c++ --version | head -n1)
if [[ "${cxx_version}" == *clang* ]]; then
local clang_major
clang_major=$(/usr/bin/c++ -dumpversion | cut -d. -f1)
if [[ "${clang_major}" =~ ^[0-9]+$ && "${clang_major}" -lt 16 ]] &&
command -v gcc >/dev/null &&
command -v g++ >/dev/null; then
export CC=gcc
export CXX=g++
fi
fi
}

install_pytorch_and_domains() {
git clone https://github.com/pytorch/pytorch.git

Expand All @@ -33,14 +48,15 @@ install_pytorch_and_domains() {
if [[ -n "${PYTORCH_BUILD_MAX_JOBS:-}" ]]; then
export MAX_JOBS="${PYTORCH_BUILD_MAX_JOBS}"
fi
configure_pytorch_compiler
# Then build and install PyTorch
conda_run python setup.py bdist_wheel
pip_install "$(echo dist/*.whl)"

# Grab the pinned audio and vision commits from PyTorch
TORCHAUDIO_VERSION=release/2.11
export TORCHAUDIO_VERSION
TORCHVISION_VERSION=release/0.27
TORCHVISION_VERSION=release/0.28
export TORCHVISION_VERSION

install_domains
Expand Down
4 changes: 4 additions & 0 deletions .ci/scripts/setup-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ conda activate et
# Install test dependencies
pip install -r .ci/docker/requirements-ci.txt

# PyTorch 2.13 headers use designated initializers in c10, which require C++20
# with MSVC.
$env:CMAKE_ARGS = "$env:CMAKE_ARGS -DCMAKE_CXX_STANDARD=20"

# The Windows CI image ships CUDA toolkits on PATH, so install_executorch
# (setup.py) auto-enables EXECUTORCH_BUILD_CUDA whenever the detected nvcc
# version is in SUPPORTED_CUDA_VERSIONS. CPU-only jobs install CPU torch, so a
Expand Down
19 changes: 16 additions & 3 deletions .ci/scripts/test_model_e2e_windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,35 @@ try {
"-DCUDAToolkit_ROOT=$env:CUDA_HOME"
)
}
$cmakeCommonArgs = @("-DCMAKE_CXX_STANDARD=20")
Write-Host "::endgroup::"

Write-Host "::group::Build ExecuTorch (CUDA)"
$numCores = [Math]::Max([Environment]::ProcessorCount - 1, 1)
$cmakeOut = Join-Path -Path $executorchRoot -ChildPath "cmake-out"
$executorchCmakeDir = Join-Path -Path $cmakeOut -ChildPath "lib\cmake\ExecuTorch"
$executorchConfig = Join-Path -Path $executorchCmakeDir -ChildPath "executorch-config.cmake"
# EXECUTORCH_BUILD_EXTENSION_IMAGE defaults OFF in the preset, so it must be
# enabled explicitly here (matching the Makefile CUDA target used on Linux).
# The dinov2 runner links the installed extension_image.lib; without this the
# main install never builds it and dinov2_runner fails to link (LNK1181).
cmake --preset llm-release-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON @cmakeCudaArgs
cmake --build cmake-out --target install --config Release -j $numCores
cmake --preset llm-release-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON @cmakeCommonArgs @cmakeCudaArgs
cmake --build $cmakeOut --config Release -j $numCores
cmake --install $cmakeOut --config Release
if (-not (Test-Path -Path $executorchConfig -PathType Leaf)) {
throw "ExecuTorch CMake package config not found after install: $executorchConfig"
}
Write-Host "::endgroup::"

Write-Host "::group::Build $runnerTarget"
$cmakePackageArgs = @(
"-DCMAKE_FIND_ROOT_PATH=$cmakeOut",
"-DCMAKE_PREFIX_PATH=$cmakeOut;$executorchCmakeDir",
"-Dexecutorch_DIR=$executorchCmakeDir"
)
Push-Location (Join-Path -Path $executorchRoot -ChildPath "examples\models\$runnerPath")
try {
cmake --preset $runnerPreset @cmakeCudaArgs
cmake --preset $runnerPreset @cmakeCommonArgs @cmakePackageArgs @cmakeCudaArgs
cmake --build (Join-Path -Path $executorchRoot -ChildPath "cmake-out\examples\models\$runnerPath") --target $runnerTarget --config Release -j $numCores
}
finally {
Expand Down
4 changes: 2 additions & 2 deletions .ci/scripts/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ install_pytorch_and_domains() {
local torch_release=$(cat version.txt)
# Download key must match the upload key below (basename of dist/*.whl,
# which always carries setup.py's resolved +gitHASH). Branch-ref pins
# like `release/2.12` would otherwise produce `+gitrelease` here and
# like `release/2.13` would otherwise produce `+gitrelease` here and
# never hit the cache.
local torch_short_hash=$(git rev-parse --short=7 HEAD)
local torch_wheel_path="cached_artifacts/pytorch/executorch/pytorch_wheels/${system_name}/${python_version}"
Expand Down Expand Up @@ -178,7 +178,7 @@ install_pytorch_and_domains() {
# Grab the pinned audio and vision commits from PyTorch
TORCHAUDIO_VERSION=release/2.11
export TORCHAUDIO_VERSION
TORCHVISION_VERSION=release/0.27
TORCHVISION_VERSION=release/0.28
export TORCHVISION_VERSION

install_domains
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/mlx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ jobs:
--prompt-len 4 \
--max-new-tokens 5 2>&1)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "Generated token ids: \[167, 94, 253, 88, 227\]"; then
if echo "$OUTPUT" | grep -q "Generated token ids: \[167, 180, 167, 180, 167\]"; then
echo "Success: Qwen 3.5 MoE MLX export + inference completed with expected output"
else
echo "Failed: unexpected output (expected [167, 94, 253, 88, 227])"
echo "Failed: unexpected output (expected [167, 180, 167, 180, 167])"
exit 1
fi
echo "::endgroup::"
Expand Down
5 changes: 5 additions & 0 deletions backends/aoti/common_shims.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim) {
return Error::Ok;
}

AOTITorchError aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel) {
*ret_numel = static_cast<int64_t>(tensor->numel());
return Error::Ok;
}

// Device and layout utility functions
int32_t aoti_torch_device_type_cpu() {
// Let's say cpu is 0 for ET as well
Expand Down
3 changes: 3 additions & 0 deletions backends/aoti/common_shims.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ aoti_torch_get_device_index(Tensor* tensor, int32_t* ret_device_index);
AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim);

AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel);

// Utility functions for device and layout information
AOTI_SHIM_EXPORT int32_t aoti_torch_device_type_cpu();
AOTI_SHIM_EXPORT int32_t aoti_torch_layout_strided();
Expand Down
8 changes: 8 additions & 0 deletions backends/aoti/common_shims_slim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ AOTITorchError aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim) {
return Error::Ok;
}

AOTITorchError aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel) {
if (tensor == nullptr || ret_numel == nullptr) {
return Error::InvalidArgument;
}
*ret_numel = static_cast<int64_t>(tensor->numel());
return Error::Ok;
}

int32_t aoti_torch_layout_strided() {
// Slimtensor only support strided layout, the return value will always be 0,
// a.k.a at::Layout::Strided;
Expand Down
3 changes: 3 additions & 0 deletions backends/aoti/common_shims_slim.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ aoti_torch_get_dtype(Tensor* tensor, int32_t* ret_dtype);
AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_dim(Tensor* tensor, int64_t* ret_dim);

AOTI_SHIM_EXPORT AOTITorchError
aoti_torch_get_numel(Tensor* tensor, int64_t* ret_numel);

AOTI_SHIM_EXPORT int32_t aoti_torch_layout_strided();

// ============================================================
Expand Down
20 changes: 20 additions & 0 deletions backends/aoti/tests/test_common_shims.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,21 @@ TEST_F(CommonShimsTest, ScalarTensor) {
error = aoti_torch_get_sizes(tensor_0d, &sizes_ptr2);
EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(sizes_ptr, sizes_ptr2);

int64_t numel = -1;
error = aoti_torch_get_numel(tensor_0d, &numel);
EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(numel, 1);
}

TEST_F(CommonShimsTest, GetNumel) {
auto tensor = create_tracked_tensor({2, 3, 4});

int64_t numel = -1;
AOTITorchError error = aoti_torch_get_numel(tensor, &numel);

EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(numel, 24);
}

// Test large tensor dimensions
Expand All @@ -266,6 +281,11 @@ TEST_F(CommonShimsTest, LargeTensorDimensions) {
EXPECT_EQ(strides_ptr[1], 120000);
EXPECT_EQ(strides_ptr[2], 400);
EXPECT_EQ(strides_ptr[3], 1);

int64_t numel = -1;
error = aoti_torch_get_numel(tensor, &numel);
EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(numel, 2400000000);
}

// Test that cleanup_tensor_metadata clears the cache
Expand Down
66 changes: 66 additions & 0 deletions backends/aoti/tests/test_common_shims_slim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,50 @@ void runGetDimTest(slim_c10::DeviceType device_type) {
}
}

void runGetNumelTest(slim_c10::DeviceType device_type) {
slim_c10::Device device(device_type, 0);

// Test 0D tensor (scalar)
{
std::vector<int64_t> sizes = {};
std::vector<int64_t> strides = {};

Tensor* tensor = new Tensor(slim::empty_strided(
slim::makeArrayRef(sizes),
slim::makeArrayRef(strides),
slim_c10::ScalarType::Float,
device));

int64_t ret_numel = -1;
AOTITorchError error = aoti_torch_get_numel(tensor, &ret_numel);

EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(ret_numel, 1);

delete tensor;
}

// Test 3D tensor
{
std::vector<int64_t> sizes = {2, 3, 4};
std::vector<int64_t> strides = calculateContiguousStrides(sizes);

Tensor* tensor = new Tensor(slim::empty_strided(
slim::makeArrayRef(sizes),
slim::makeArrayRef(strides),
slim_c10::ScalarType::Float,
device));

int64_t ret_numel = -1;
AOTITorchError error = aoti_torch_get_numel(tensor, &ret_numel);

EXPECT_EQ(error, Error::Ok);
EXPECT_EQ(ret_numel, 24);

delete tensor;
}
}

// ============================================================================
// Storage & Device Property Tests
// ============================================================================
Expand Down Expand Up @@ -400,6 +444,10 @@ TEST_F(CommonShimsSlimTest, GetDim_CPU) {
runGetDimTest(slim_c10::DeviceType::CPU);
}

TEST_F(CommonShimsSlimTest, GetNumel_CPU) {
runGetNumelTest(slim_c10::DeviceType::CPU);
}

TEST_F(CommonShimsSlimTest, GetStorageOffset_CPU) {
runGetStorageOffsetTest(slim_c10::DeviceType::CPU);
}
Expand Down Expand Up @@ -456,6 +504,13 @@ TEST_F(CommonShimsSlimTest, GetDim_CUDA) {
runGetDimTest(slim_c10::DeviceType::CUDA);
}

TEST_F(CommonShimsSlimTest, GetNumel_CUDA) {
if (!isCudaAvailable()) {
GTEST_SKIP() << "CUDA not available";
}
runGetNumelTest(slim_c10::DeviceType::CUDA);
}

TEST_F(CommonShimsSlimTest, GetStorageOffset_CUDA) {
if (!isCudaAvailable()) {
GTEST_SKIP() << "CUDA not available";
Expand Down Expand Up @@ -495,13 +550,15 @@ TEST_F(CommonShimsSlimTest, NullTensorArgument) {
int64_t* strides = nullptr;
int32_t dtype = -1;
int64_t dim = -1;
int64_t numel = -1;

EXPECT_EQ(
aoti_torch_get_data_ptr(nullptr, &data_ptr), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_sizes(nullptr, &sizes), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_strides(nullptr, &strides), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_dtype(nullptr, &dtype), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_dim(nullptr, &dim), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_numel(nullptr, &numel), Error::InvalidArgument);
}

TEST_F(CommonShimsSlimTest, NullReturnPointer) {
Expand All @@ -512,6 +569,7 @@ TEST_F(CommonShimsSlimTest, NullReturnPointer) {
EXPECT_EQ(aoti_torch_get_strides(tensor, nullptr), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_dtype(tensor, nullptr), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_dim(tensor, nullptr), Error::InvalidArgument);
EXPECT_EQ(aoti_torch_get_numel(tensor, nullptr), Error::InvalidArgument);
}

// ============================================================================
Expand All @@ -534,6 +592,7 @@ TEST_F(CommonShimsSlimTest, ScalarTensor) {
int64_t* ret_sizes = nullptr;
int64_t* ret_strides = nullptr;
int64_t ret_dim = -1;
int64_t ret_numel = -1;

EXPECT_EQ(aoti_torch_get_sizes(tensor, &ret_sizes), Error::Ok);
EXPECT_NE(ret_sizes, nullptr);
Expand All @@ -543,6 +602,9 @@ TEST_F(CommonShimsSlimTest, ScalarTensor) {

EXPECT_EQ(aoti_torch_get_dim(tensor, &ret_dim), Error::Ok);
EXPECT_EQ(ret_dim, 0);

EXPECT_EQ(aoti_torch_get_numel(tensor, &ret_numel), Error::Ok);
EXPECT_EQ(ret_numel, 1);
}

TEST_F(CommonShimsSlimTest, LargeTensor) {
Expand All @@ -559,6 +621,7 @@ TEST_F(CommonShimsSlimTest, LargeTensor) {

int64_t* ret_sizes = nullptr;
int64_t* ret_strides = nullptr;
int64_t ret_numel = -1;

EXPECT_EQ(aoti_torch_get_sizes(tensor, &ret_sizes), Error::Ok);
EXPECT_EQ(ret_sizes[0], 100);
Expand All @@ -569,6 +632,9 @@ TEST_F(CommonShimsSlimTest, LargeTensor) {
EXPECT_EQ(ret_strides[0], 60000); // 200 * 300
EXPECT_EQ(ret_strides[1], 300); // 300
EXPECT_EQ(ret_strides[2], 1);

EXPECT_EQ(aoti_torch_get_numel(tensor, &ret_numel), Error::Ok);
EXPECT_EQ(ret_numel, 6000000);
}

TEST_F(CommonShimsSlimTest, ConsistentPointerReturn) {
Expand Down
22 changes: 21 additions & 1 deletion backends/cuda/cuda_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ def _move_to_device_resize_kv(ep, location):
return ep


def _on_off_compile_spec_value(spec: CompileSpec) -> bool:
value = spec.value.decode("utf-8").upper()
if value not in ["ON", "OFF"]:
raise ValueError(f"Invalid {spec.key}: {value}. Expected 'ON' or 'OFF'.")
return value == "ON"


@final
@experimental(
"This API and all of cuda backend related functionality are experimental."
Expand Down Expand Up @@ -496,12 +503,25 @@ def get_aoti_compile_options(
# Parse compile_specs to check for platform

platform = "linux"
emulate_precision_casts = True
max_autotune = True
autotune_at_compile_time = None
shim_library_path = None
for spec in compile_specs:
if spec.key == "platform":
platform = spec.value.decode("utf-8")
if spec.key == "shim_library_path":
elif spec.key == "emulate_precision_casts":
emulate_precision_casts = _on_off_compile_spec_value(spec)
elif spec.key == "max_autotune":
max_autotune = _on_off_compile_spec_value(spec)
elif spec.key == "autotune_at_compile_time":
autotune_at_compile_time = _on_off_compile_spec_value(spec)
elif spec.key == "shim_library_path":
shim_library_path = spec.value.decode("utf-8")
options["emulate_precision_casts"] = emulate_precision_casts
options["max_autotune"] = max_autotune
if autotune_at_compile_time is not None:
options["triton.autotune_at_compile_time"] = autotune_at_compile_time
# Add platform-specific options

if platform == "windows":
Expand Down
Binary file modified backends/cuda/runtime/aoti_cuda_shims.lib
Binary file not shown.
Loading
Loading