From 49722c84181f5699e84db36558933055da20e043 Mon Sep 17 00:00:00 2001 From: Gregory James Comer Date: Wed, 3 Sep 2025 17:42:29 -0700 Subject: [PATCH 1/3] [Windows] Run native unit tests in CI ghstack-source-id: 1dca42f99e11914cb3ab537c53e0d276aeb9bc24 ghstack-comment-id: 3251228740 Pull-Request: https://github.com/pytorch/executorch/pull/13925 --- .ci/scripts/unittest-windows.ps1 | 27 ++++++++++++++++++++++++--- .github/workflows/_unittest.yml | 4 ++-- .github/workflows/trunk.yml | 2 +- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.ci/scripts/unittest-windows.ps1 b/.ci/scripts/unittest-windows.ps1 index 65ed303051b..adc6c494a9d 100644 --- a/.ci/scripts/unittest-windows.ps1 +++ b/.ci/scripts/unittest-windows.ps1 @@ -6,10 +6,31 @@ Set-PSDebug -Trace 1 $ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = $true -# Run pytest with coverage -# pytest -n auto --cov=./ --cov-report=xml -pytest -v --full-trace -c pytest-windows.ini +# Run pytest +pytest -v -c pytest-windows.ini if ($LASTEXITCODE -ne 0) { Write-Host "Pytest invocation was unsuccessful. Exit code: $LASTEXITCODE." exit $LASTEXITCODE } + +# Run native unit tests (via ctest) +New-Item -Path "test-build" -ItemType Directory +cd "test-build" + +cmake .. --preset windows -B . -DCMAKE_BUILD_TESTS=ON +if ($LASTEXITCODE -ne 0) { + Write-Host "CMake configuration was unsuccessful. Exit code: $LASTEXITCODE." + exit $LASTEXITCODE +} + +cmake --build . -j8 --config Release +if ($LASTEXITCODE -ne 0) { + Write-Host "CMake build was unsuccessful. Exit code: $LASTEXITCODE." + exit $LASTEXITCODE +} + +ctest -j8 +if ($LASTEXITCODE -ne 0) { + Write-Host "CTest run was unsuccessful. Exit code: $LASTEXITCODE." + exit $LASTEXITCODE +} diff --git a/.github/workflows/_unittest.yml b/.github/workflows/_unittest.yml index a619b33dd2e..52c121099f1 100644 --- a/.github/workflows/_unittest.yml +++ b/.github/workflows/_unittest.yml @@ -69,7 +69,7 @@ jobs: \$ErrorActionPreference = 'Stop' \$PSNativeCommandUseErrorActionPreference = \$true - .ci/scripts/setup-windows.ps1 + .ci/scripts/setup-windows.ps1 - powershell .ci/scripts/unittest-windows.ps1 -editable "${{ inputs.editable }}" + .ci/scripts/unittest-windows.ps1 -editable "${{ inputs.editable }}" }" diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml index f5c5161e0cc..975a8ebbb30 100644 --- a/.github/workflows/trunk.yml +++ b/.github/workflows/trunk.yml @@ -1032,5 +1032,5 @@ jobs: .ci/scripts/setup-windows.ps1 - powershell .ci/scripts/test_model.ps1 -modelName ${{ matrix.model }} -backend ${{ matrix.backend }} + .ci/scripts/test_model.ps1 -modelName ${{ matrix.model }} -backend ${{ matrix.backend }} }" From e9e749b5532d8415e8f21e3466b7dbc9c25cb744 Mon Sep 17 00:00:00 2001 From: Gregory Comer Date: Thu, 4 Sep 2025 17:42:55 -0700 Subject: [PATCH 2/3] Fix cmake option for tests --- .ci/scripts/setup-windows.ps1 | 2 +- .ci/scripts/unittest-windows.ps1 | 6 +++--- .github/workflows/_unittest.yml | 12 ++++++++++-- CMakeLists.txt | 10 +++++++--- runtime/executor/test/CMakeLists.txt | 2 +- test/end2end/exported_module.py | 1 - test/models/export_delegated_program.py | 8 ++++---- tools/cmake/Utils.cmake | 2 ++ 8 files changed, 28 insertions(+), 15 deletions(-) diff --git a/.ci/scripts/setup-windows.ps1 b/.ci/scripts/setup-windows.ps1 index 20d29e4f558..329e81b3cf0 100644 --- a/.ci/scripts/setup-windows.ps1 +++ b/.ci/scripts/setup-windows.ps1 @@ -1,5 +1,5 @@ param ( - [string]$editable = $false + [string]$editable = "false" ) conda create --yes --quiet -n et python=3.12 diff --git a/.ci/scripts/unittest-windows.ps1 b/.ci/scripts/unittest-windows.ps1 index adc6c494a9d..785ce929a70 100644 --- a/.ci/scripts/unittest-windows.ps1 +++ b/.ci/scripts/unittest-windows.ps1 @@ -1,5 +1,5 @@ param ( - [string]$editable = $false + [string]$buildMode = "Release" ) Set-PSDebug -Trace 1 @@ -17,13 +17,13 @@ if ($LASTEXITCODE -ne 0) { New-Item -Path "test-build" -ItemType Directory cd "test-build" -cmake .. --preset windows -B . -DCMAKE_BUILD_TESTS=ON +cmake .. --preset windows -B . -DEXECUTORCH_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=$buildMode if ($LASTEXITCODE -ne 0) { Write-Host "CMake configuration was unsuccessful. Exit code: $LASTEXITCODE." exit $LASTEXITCODE } -cmake --build . -j8 --config Release +cmake --build . -j8 --config $buildMode --verbose if ($LASTEXITCODE -ne 0) { Write-Host "CMake build was unsuccessful. Exit code: $LASTEXITCODE." exit $LASTEXITCODE diff --git a/.github/workflows/_unittest.yml b/.github/workflows/_unittest.yml index 52c121099f1..587f2cf5e5a 100644 --- a/.github/workflows/_unittest.yml +++ b/.github/workflows/_unittest.yml @@ -69,7 +69,15 @@ jobs: \$ErrorActionPreference = 'Stop' \$PSNativeCommandUseErrorActionPreference = \$true - .ci/scripts/setup-windows.ps1 + .ci/scripts/setup-windows.ps1 -editable "${{ inputs.editable }}" + if (\$LASTEXITCODE -ne 0) { + Write-Host "Setup failed. Exit code: \$LASTEXITCODE." + exit \$LASTEXITCODE + } - .ci/scripts/unittest-windows.ps1 -editable "${{ inputs.editable }}" + .ci/scripts/unittest-windows.ps1 -buildMode "${{ inputs.build-mode }}" + if (\$LASTEXITCODE -ne 0) { + Write-Host "Unit tests failed. Exit code: \$LASTEXITCODE." + exit \$LASTEXITCODE + } }" diff --git a/CMakeLists.txt b/CMakeLists.txt index 2664b4491c9..fc427d517a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,9 +143,13 @@ endif() # -ffunction-sections -fdata-sections: breaks function and data into sections so # they can be properly gc'd. -s: strip symbol. -set(CMAKE_CXX_FLAGS_RELEASE - "-ffunction-sections -fdata-sections ${CMAKE_CXX_FLAGS_RELEASE}" -) +if(WIN32) + set(CMAKE_CXX_FLAGS_RELEASE "/Gy /Gw ${CMAKE_CXX_FLAGS_RELEASE}") +else() + set(CMAKE_CXX_FLAGS_RELEASE + "-ffunction-sections -fdata-sections ${CMAKE_CXX_FLAGS_RELEASE}" + ) +endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") endif() diff --git a/runtime/executor/test/CMakeLists.txt b/runtime/executor/test/CMakeLists.txt index d8df1f9ea56..12d04c382ad 100644 --- a/runtime/executor/test/CMakeLists.txt +++ b/runtime/executor/test/CMakeLists.txt @@ -39,7 +39,7 @@ add_custom_command( COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_delegated_program --modules "ModuleAddMul" --backend_id "StubBackend" --outdir - "${CMAKE_CURRENT_BINARY_DIR}/delegated/" || true + "${CMAKE_CURRENT_BINARY_DIR}/delegated/" WORKING_DIRECTORY ${EXECUTORCH_ROOT} ) diff --git a/test/end2end/exported_module.py b/test/end2end/exported_module.py index e5630b8e89f..750b9097335 100644 --- a/test/end2end/exported_module.py +++ b/test/end2end/exported_module.py @@ -187,7 +187,6 @@ def __init__(self, method): if method_name_to_dynamic_shapes else None ), - strict=True, ) exec_prog = to_edge( diff --git a/test/models/export_delegated_program.py b/test/models/export_delegated_program.py index 8f7c388d7ad..98f4b0b9b36 100644 --- a/test/models/export_delegated_program.py +++ b/test/models/export_delegated_program.py @@ -155,9 +155,9 @@ def forward(self, *args, **kwargs): if method_name != "forward": # Only require wrapper module if we're exporting a specific method other than forward. - exported_program = export(WrapperModule(eager_module), args=inputs, strict=True) + exported_program = export(WrapperModule(eager_module), args=inputs) else: - exported_program = export(eager_module, args=inputs, strict=True) + exported_program = export(eager_module, args=inputs) edge_config = EdgeCompileConfig(_check_ir_validity=False) et_config = exir.ExecutorchBackendConfig( @@ -178,7 +178,7 @@ def forward(self, *args, **kwargs): module=tagged_module, gen_tag_fn=lambda x: module_class.__name__, ) - exported_program = export(tagged_module, args=inputs, strict=True) + exported_program = export(tagged_module, args=inputs) executorch_program = to_edge_transform_and_lower( exported_program, compile_config=edge_config, @@ -205,7 +205,7 @@ def forward(self, *args, **kwargs): composite_module(*inputs) executorch_program = to_edge( - export(composite_module, args=inputs, strict=True) + export(composite_module, args=inputs) ).to_executorch(config=et_config) return executorch_program diff --git a/tools/cmake/Utils.cmake b/tools/cmake/Utils.cmake index 1e0671eb920..77918ebbf2e 100644 --- a/tools/cmake/Utils.cmake +++ b/tools/cmake/Utils.cmake @@ -62,6 +62,8 @@ endfunction() function(target_link_options_gc_sections target_name) if(APPLE) target_link_options(${target_name} PRIVATE "LINKER:-dead_strip") + elseif(WIN32) + target_link_options(${target_name} PRIVATE "LINKER:/OPT:REF") else() target_link_options(${target_name} PRIVATE "LINKER:--gc-sections") endif() From 04826a7de3cb42b72bf9389ac7e0a5074e0404f6 Mon Sep 17 00:00:00 2001 From: Gregory Comer Date: Tue, 9 Sep 2025 21:09:44 -0700 Subject: [PATCH 3/3] More fixes and enablement --- .ci/scripts/unittest-windows.ps1 | 18 +++--- backends/xnnpack/CMakeLists.txt | 2 +- export/target_recipes.py | 26 +++++---- export/tests/test_target_recipes.py | 14 ++++- .../evalue_util/test/print_evalue_test.cpp | 4 +- extension/flat_tensor/test/CMakeLists.txt | 2 +- extension/module/test/CMakeLists.txt | 4 +- extension/runner_util/test/CMakeLists.txt | 2 +- extension/testing_util/temp_file.h | 24 +++----- kernels/portable/cpu/op_argmax.cpp | 8 +-- kernels/portable/cpu/op_argmin.cpp | 8 +-- kernels/portable/cpu/op_clamp.cpp | 4 +- kernels/portable/cpu/op_gather.cpp | 2 +- kernels/portable/cpu/op_max.cpp | 8 +-- kernels/portable/cpu/op_min.cpp | 8 +-- kernels/portable/cpu/op_scatter.cpp | 4 +- kernels/portable/cpu/op_scatter_add.cpp | 4 +- kernels/portable/cpu/op_topk.cpp | 2 +- kernels/test/CMakeLists.txt | 32 ++++++++--- kernels/test/export_test_model.ps1 | 24 ++++++++ pytest-windows.ini | 2 + .../exec_aten/testing_util/tensor_util.cpp | 56 +++++++++++++------ runtime/executor/test/CMakeLists.txt | 30 ++++++++-- runtime/kernel/test/CMakeLists.txt | 6 -- 24 files changed, 192 insertions(+), 102 deletions(-) create mode 100644 kernels/test/export_test_model.ps1 diff --git a/.ci/scripts/unittest-windows.ps1 b/.ci/scripts/unittest-windows.ps1 index 785ce929a70..6f1365bc3fc 100644 --- a/.ci/scripts/unittest-windows.ps1 +++ b/.ci/scripts/unittest-windows.ps1 @@ -6,13 +6,6 @@ Set-PSDebug -Trace 1 $ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = $true -# Run pytest -pytest -v -c pytest-windows.ini -if ($LASTEXITCODE -ne 0) { - Write-Host "Pytest invocation was unsuccessful. Exit code: $LASTEXITCODE." - exit $LASTEXITCODE -} - # Run native unit tests (via ctest) New-Item -Path "test-build" -ItemType Directory cd "test-build" @@ -29,8 +22,17 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } -ctest -j8 +ctest -j8 . --build-config $buildMode --output-on-failure -E "method_test|tensor_parser_test" if ($LASTEXITCODE -ne 0) { Write-Host "CTest run was unsuccessful. Exit code: $LASTEXITCODE." exit $LASTEXITCODE } + +cd .. + +# Run pytest +pytest -v -c pytest-windows.ini +if ($LASTEXITCODE -ne 0) { + Write-Host "Pytest invocation was unsuccessful. Exit code: $LASTEXITCODE." + exit $LASTEXITCODE +} \ No newline at end of file diff --git a/backends/xnnpack/CMakeLists.txt b/backends/xnnpack/CMakeLists.txt index 200d8987b19..33bf84b9066 100644 --- a/backends/xnnpack/CMakeLists.txt +++ b/backends/xnnpack/CMakeLists.txt @@ -59,7 +59,7 @@ foreach(fbs_file ${_xnnpack_schema__srcs}) ) endforeach() -if(WIN32 AND NOT CMAKE_CROSSCOMPILING) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") set(MV_COMMAND powershell -Command "Move-Item -Path ${_xnnpack_flatbuffer__outputs} -Destination ${_xnnpack_schema__outputs} -Force" diff --git a/export/target_recipes.py b/export/target_recipes.py index 76e0cacc7b4..0a5ae9ce754 100644 --- a/export/target_recipes.py +++ b/export/target_recipes.py @@ -11,26 +11,32 @@ selection and combine multiple backends optimally for target hardware. """ +import sys from typing import Dict, List -import coremltools as ct +if sys.platform != "win32": + import coremltools as ct + from executorch.backends.apple.coreml.recipes import CoreMLRecipeType # pyre-ignore -from executorch.backends.apple.coreml.recipes import CoreMLRecipeType from executorch.backends.xnnpack.recipes import XNNPackRecipeType from executorch.export.recipe import ExportRecipe, RecipeType ## IOS Target configs # The following list of recipes are not exhaustive for CoreML; refer to CoreMLRecipeType for more detailed recipes. -IOS_CONFIGS: Dict[str, List[RecipeType]] = { - # pyre-ignore - "ios-arm64-coreml-fp32": [CoreMLRecipeType.FP32, XNNPackRecipeType.FP32], - # pyre-ignore - "ios-arm64-coreml-fp16": [CoreMLRecipeType.FP16], - # pyre-ignore - "ios-arm64-coreml-int8": [CoreMLRecipeType.PT2E_INT8_STATIC], -} +IOS_CONFIGS: Dict[str, List[RecipeType]] = ( + { + # pyre-ignore + "ios-arm64-coreml-fp32": [CoreMLRecipeType.FP32, XNNPackRecipeType.FP32], + # pyre-ignore + "ios-arm64-coreml-fp16": [CoreMLRecipeType.FP16], + # pyre-ignore + "ios-arm64-coreml-int8": [CoreMLRecipeType.PT2E_INT8_STATIC], + } + if sys.platform != "win32" + else {} +) def _create_target_recipe( diff --git a/export/tests/test_target_recipes.py b/export/tests/test_target_recipes.py index d781ffea945..7a2a7c87342 100644 --- a/export/tests/test_target_recipes.py +++ b/export/tests/test_target_recipes.py @@ -7,10 +7,10 @@ # pyre-strict import logging +import sys import unittest import torch -from executorch.backends.apple.coreml.recipes import CoreMLRecipeProvider # pyre-ignore from executorch.backends.xnnpack.recipes.xnnpack_recipe_provider import ( XNNPACKRecipeProvider, ) @@ -18,6 +18,11 @@ from executorch.export.target_recipes import get_ios_recipe from executorch.runtime import Runtime +if sys.platform != "win32": + from executorch.backends.apple.coreml.recipes import ( # pyre-ignore + CoreMLRecipeProvider, + ) + class TestTargetRecipes(unittest.TestCase): """Test target recipes.""" @@ -26,12 +31,14 @@ def setUp(self) -> None: torch._dynamo.reset() super().setUp() recipe_registry.register_backend_recipe_provider(XNNPACKRecipeProvider()) - # pyre-ignore - recipe_registry.register_backend_recipe_provider(CoreMLRecipeProvider()) + if sys.platform != "win32": + # pyre-ignore + recipe_registry.register_backend_recipe_provider(CoreMLRecipeProvider()) def tearDown(self) -> None: super().tearDown() + @unittest.skipIf(sys.platform == "win32", "Core ML is not available on Windows.") def test_ios_fp32_recipe_with_xnnpack_fallback(self) -> None: # Linear ops skipped by coreml but handled by xnnpack class Model(torch.nn.Module): @@ -107,6 +114,7 @@ def forward(self, x, y): et_output = session.run_method("forward", example_inputs[0]) logging.info(f"et output {et_output}") + @unittest.skipIf(sys.platform == "win32", "Core ML is not available on Windows.") def test_ios_quant_recipes(self) -> None: class Model(torch.nn.Module): def __init__(self): diff --git a/extension/evalue_util/test/print_evalue_test.cpp b/extension/evalue_util/test/print_evalue_test.cpp index b881e55d8a8..242cb0af224 100644 --- a/extension/evalue_util/test/print_evalue_test.cpp +++ b/extension/evalue_util/test/print_evalue_test.cpp @@ -267,7 +267,7 @@ TEST(PrintEvalueTest, UnelidedBoolLists) { // case; the other scalar types use the same underlying code, so they don't // need to test this again. { - EValue value(ArrayRef(list.data(), 0ul)); + EValue value(ArrayRef(list.data(), static_cast(0ul))); expect_output(value, "(len=0)[]"); } { @@ -419,7 +419,7 @@ TEST(PrintEvalueTest, UnelidedDoubleLists) { std::array list = {-2.2, -1, 0, INFINITY, NAN, 3.3}; { - EValue value(ArrayRef(list.data(), 0ul)); + EValue value(ArrayRef(list.data(), static_cast(0ul))); expect_output(value, "(len=0)[]"); } { diff --git a/extension/flat_tensor/test/CMakeLists.txt b/extension/flat_tensor/test/CMakeLists.txt index c3296dc61f3..fd3d6792f90 100644 --- a/extension/flat_tensor/test/CMakeLists.txt +++ b/extension/flat_tensor/test/CMakeLists.txt @@ -23,7 +23,7 @@ add_custom_command( "${CMAKE_CURRENT_BINARY_DIR}/ModuleAddMulProgram.ptd" COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_program --modules "ModuleAddMul" - --external-constants --outdir "${CMAKE_CURRENT_BINARY_DIR}" 2> /dev/null + --external-constants --outdir "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY ${EXECUTORCH_ROOT} ) diff --git a/extension/module/test/CMakeLists.txt b/extension/module/test/CMakeLists.txt index 964b810eed5..1c4358dd73e 100644 --- a/extension/module/test/CMakeLists.txt +++ b/extension/module/test/CMakeLists.txt @@ -24,10 +24,10 @@ add_custom_command( "${CMAKE_CURRENT_BINARY_DIR}/ModuleAddMulProgram.pte" "${CMAKE_CURRENT_BINARY_DIR}/ModuleAddMulProgram.ptd" COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_program --modules - "ModuleAdd" --outdir "${CMAKE_CURRENT_BINARY_DIR}" 2> /dev/null + "ModuleAdd" --outdir "${CMAKE_CURRENT_BINARY_DIR}" COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_program --modules "ModuleAddMul" - --external-constants --outdir "${CMAKE_CURRENT_BINARY_DIR}" 2> /dev/null + --external-constants --outdir "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY ${EXECUTORCH_ROOT} ) diff --git a/extension/runner_util/test/CMakeLists.txt b/extension/runner_util/test/CMakeLists.txt index 0cca06178cd..44b85a7fced 100644 --- a/extension/runner_util/test/CMakeLists.txt +++ b/extension/runner_util/test/CMakeLists.txt @@ -20,7 +20,7 @@ include(${EXECUTORCH_ROOT}/tools/cmake/Test.cmake) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ModuleAdd.pte" COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_program --modules - "ModuleAdd" --outdir "${CMAKE_CURRENT_BINARY_DIR}" 2> /dev/null + "ModuleAdd" --outdir "${CMAKE_CURRENT_BINARY_DIR}" WORKING_DIRECTORY ${EXECUTORCH_ROOT} ) diff --git a/extension/testing_util/temp_file.h b/extension/testing_util/temp_file.h index aa8f5bcc82e..4edaf2135d8 100644 --- a/extension/testing_util/temp_file.h +++ b/extension/testing_util/temp_file.h @@ -9,13 +9,11 @@ #pragma once #include +#include #include #include #include // open() -#include // tmpnam(), remove() -#include // write(), close() - #include namespace executorch { @@ -72,19 +70,13 @@ class TempFile { } // Write the contents to the file. - int fd = open( - path.c_str(), - // O_EXCL ensures that we are the ones creating this file, to help - // protect against race conditions. - O_CREAT | O_EXCL | O_RDWR, - // User can read and write, group can read. - S_IRUSR | S_IWUSR | S_IRGRP); - ASSERT_GE(fd, 0) << "open(" << path << ") failed: " << strerror(errno); - - ssize_t nwrite = write(fd, data, size); - ASSERT_EQ(nwrite, size) << "Failed to write " << size << " bytes (wrote " - << nwrite << "): " << strerror(errno); - close(fd); + std::ofstream file(path, std::ios::out | std::ios::binary); + ASSERT_TRUE(file.is_open()) + << "open(" << path << ") failed: " << strerror(errno); + + file.write((const char*)data, size); + ASSERT_TRUE(file.good()) + << "Failed to write " << size << " bytes: " << strerror(errno); *out_path = path; } diff --git a/kernels/portable/cpu/op_argmax.cpp b/kernels/portable/cpu/op_argmax.cpp index e9a561366f7..a48c152133b 100644 --- a/kernels/portable/cpu/op_argmax.cpp +++ b/kernels/portable/cpu/op_argmax.cpp @@ -49,13 +49,13 @@ Tensor& argmax_out( static constexpr const char op_name[] = "argmax.out"; ET_SWITCH_REALHBF16_TYPES(in.scalar_type(), ctx, op_name, CTYPE, [&] { - long* out_data = out.mutable_data_ptr(); + int64_t* out_data = out.mutable_data_ptr(); const bool success = parallel_for_each_reduce_over_dim_output_index( in, dim, out, [&](const auto begin, const auto end) { for (const auto out_ix : c10::irange(begin, end)) { - std::tuple acc = reduce_over_dim( - [](CTYPE v, long ix, CTYPE acc_val, long acc_ix) { + std::tuple acc = reduce_over_dim( + [](CTYPE v, int64_t ix, CTYPE acc_val, int64_t acc_ix) { // the below condition as written is equivalent to // !isnan(accval) && (isnan(v) || v > acc_val). See // argument in op_argmin.cpp. @@ -63,7 +63,7 @@ Tensor& argmax_out( acc_val = v; acc_ix = ix; } - return std::tuple{acc_val, acc_ix}; + return std::tuple{acc_val, acc_ix}; }, in, dim, diff --git a/kernels/portable/cpu/op_argmin.cpp b/kernels/portable/cpu/op_argmin.cpp index fda9463c5ee..55f2f82b04b 100644 --- a/kernels/portable/cpu/op_argmin.cpp +++ b/kernels/portable/cpu/op_argmin.cpp @@ -49,13 +49,13 @@ Tensor& argmin_out( static constexpr const char op_name[] = "argmin.out"; ET_SWITCH_REALHBF16_TYPES(in.scalar_type(), ctx, op_name, CTYPE, [&] { - long* out_data = out.mutable_data_ptr(); + int64_t* out_data = out.mutable_data_ptr(); const bool success = parallel_for_each_reduce_over_dim_output_index( in, dim, out, [&](const auto begin, const auto end) { for (const auto out_ix : c10::irange(begin, end)) { - std::tuple acc = reduce_over_dim( - [](CTYPE v, long ix, CTYPE acc_val, long acc_ix) { + std::tuple acc = reduce_over_dim( + [](CTYPE v, int64_t ix, CTYPE acc_val, int64_t acc_ix) { // the below condition as written is equivalent to // !isnan(accval) && (isnan(v) || v < acc_val). cases: // - if neither acc_val nor v is NaN, !(v >= acc_val) is @@ -70,7 +70,7 @@ Tensor& argmin_out( acc_val = v; acc_ix = ix; } - return std::tuple{acc_val, acc_ix}; + return std::tuple{acc_val, acc_ix}; }, in, dim, diff --git a/kernels/portable/cpu/op_clamp.cpp b/kernels/portable/cpu/op_clamp.cpp index b3aa41cda85..8ac78fd5477 100644 --- a/kernels/portable/cpu/op_clamp.cpp +++ b/kernels/portable/cpu/op_clamp.cpp @@ -45,9 +45,9 @@ ET_NODISCARD bool check_bounds( static constexpr const char op_name[] = "clamp.out"; if (isIntegralType(out_type, /*includeBool=*/false)) { - const long val_long = utils::scalar_to(val_scalar); + const int64_t val_long = utils::scalar_to(val_scalar); ET_SWITCH_INT_TYPES(out_type, ctx, op_name, CTYPE_OUT, [&]() { - if (is_out_of_bounds(val_long)) { + if (is_out_of_bounds(val_long)) { ET_LOG(Error, "%s value out of bounds", val_name); is_valid = false; } diff --git a/kernels/portable/cpu/op_gather.cpp b/kernels/portable/cpu/op_gather.cpp index 9899c21a94e..02ea502ca63 100644 --- a/kernels/portable/cpu/op_gather.cpp +++ b/kernels/portable/cpu/op_gather.cpp @@ -30,7 +30,7 @@ void gather_helper( Tensor& out, int64_t dim) { const CTYPE* in_data = in.const_data_ptr(); - const long* index_data = index.const_data_ptr(); + const int64_t* index_data = index.const_data_ptr(); CTYPE* out_data = out.mutable_data_ptr(); if (index.dim() == 0) { diff --git a/kernels/portable/cpu/op_max.cpp b/kernels/portable/cpu/op_max.cpp index 7df93470d39..467c8ccffd5 100644 --- a/kernels/portable/cpu/op_max.cpp +++ b/kernels/portable/cpu/op_max.cpp @@ -82,19 +82,19 @@ std::tuple max_out( ET_SWITCH_REALHBBF16_TYPES( in.scalar_type(), ctx, "max.dim_max", CTYPE, [&]() { CTYPE* max_data = max.mutable_data_ptr(); - long* max_indices_data = max_indices.mutable_data_ptr(); + int64_t* max_indices_data = max_indices.mutable_data_ptr(); const bool success = parallel_for_each_reduce_over_dim_output_index( in, dim, max, [&](const auto begin, const auto end) { for (const auto out_ix : c10::irange(begin, end)) { - std::tuple acc = reduce_over_dim( - [](CTYPE v, long ix, CTYPE acc_val, long acc_ix) { + std::tuple acc = reduce_over_dim( + [](CTYPE v, int64_t ix, CTYPE acc_val, int64_t acc_ix) { if (!utils::isnan_override(acc_val) && (utils::isnan_override(v) || v > acc_val)) { acc_val = v; acc_ix = ix; } - return std::tuple{acc_val, acc_ix}; + return std::tuple{acc_val, acc_ix}; }, in, dim, diff --git a/kernels/portable/cpu/op_min.cpp b/kernels/portable/cpu/op_min.cpp index a4cd1be2067..304321bb9f8 100644 --- a/kernels/portable/cpu/op_min.cpp +++ b/kernels/portable/cpu/op_min.cpp @@ -82,19 +82,19 @@ std::tuple min_out( ET_SWITCH_REALHBBF16_TYPES( in.scalar_type(), ctx, "min.dim_min", CTYPE, [&]() { CTYPE* min_data = min.mutable_data_ptr(); - long* min_indices_data = min_indices.mutable_data_ptr(); + int64_t* min_indices_data = min_indices.mutable_data_ptr(); const bool success = parallel_for_each_reduce_over_dim_output_index( in, dim, min, [&](const auto begin, const auto end) { for (const auto out_ix : c10::irange(begin, end)) { - std::tuple acc = reduce_over_dim( - [](CTYPE v, long ix, CTYPE acc_val, long acc_ix) { + std::tuple acc = reduce_over_dim( + [](CTYPE v, int64_t ix, CTYPE acc_val, int64_t acc_ix) { if (!utils::isnan_override(acc_val) && (utils::isnan_override(v) || v < acc_val)) { acc_val = v; acc_ix = ix; } - return std::tuple{acc_val, acc_ix}; + return std::tuple{acc_val, acc_ix}; }, in, dim, diff --git a/kernels/portable/cpu/op_scatter.cpp b/kernels/portable/cpu/op_scatter.cpp index 58341cefb1e..42d40c8284d 100644 --- a/kernels/portable/cpu/op_scatter.cpp +++ b/kernels/portable/cpu/op_scatter.cpp @@ -32,7 +32,7 @@ void scatter_src_helper( const Tensor& src, Tensor& out) { const CTYPE* in_data = in.const_data_ptr(); - const long* index_data = index.const_data_ptr(); + const int64_t* index_data = index.const_data_ptr(); const CTYPE* src_data = src.const_data_ptr(); CTYPE* out_data = out.mutable_data_ptr(); @@ -72,7 +72,7 @@ void scatter_value_helper( CTYPE_VAL val, Tensor& out) { const CTYPE* in_data = in.const_data_ptr(); - const long* index_data = index.const_data_ptr(); + const int64_t* index_data = index.const_data_ptr(); CTYPE* out_data = out.mutable_data_ptr(); memcpy(out_data, in_data, in.nbytes()); diff --git a/kernels/portable/cpu/op_scatter_add.cpp b/kernels/portable/cpu/op_scatter_add.cpp index f9c1f7677b6..690c31342a9 100644 --- a/kernels/portable/cpu/op_scatter_add.cpp +++ b/kernels/portable/cpu/op_scatter_add.cpp @@ -23,7 +23,7 @@ namespace { template void scatter_add_helper( const CTYPE* src_data, - const long* index_data, + const int64_t* index_data, CTYPE* out_data, const Tensor& src, const Tensor& index, @@ -81,7 +81,7 @@ Tensor& scatter_add_out( ET_SWITCH_REALHBBF16_TYPES(self_type, ctx, "scatter_add.out", CTYPE, [&]() { const CTYPE* self_data = self.const_data_ptr(); - const long* index_data = index.const_data_ptr(); + const int64_t* index_data = index.const_data_ptr(); const CTYPE* src_data = src.const_data_ptr(); CTYPE* out_data = out.mutable_data_ptr(); diff --git a/kernels/portable/cpu/op_topk.cpp b/kernels/portable/cpu/op_topk.cpp index e2143ce78d5..bdea02f83bc 100644 --- a/kernels/portable/cpu/op_topk.cpp +++ b/kernels/portable/cpu/op_topk.cpp @@ -79,7 +79,7 @@ void perform_topk( elem_t* queue) { const CTYPE* const in_data = in.const_data_ptr(); CTYPE* values_data = values.mutable_data_ptr(); - long* indices_data = indices.mutable_data_ptr(); + int64_t* indices_data = indices.mutable_data_ptr(); if (in.dim() == 0) { values_data[0] = in_data[0]; diff --git a/kernels/test/CMakeLists.txt b/kernels/test/CMakeLists.txt index 0304d751455..2e488b109c1 100644 --- a/kernels/test/CMakeLists.txt +++ b/kernels/test/CMakeLists.txt @@ -26,8 +26,8 @@ foreach(kernel ${_kernels}) set(_functions_include "#include ") add_custom_command( OUTPUT "${_wrapper_path}" - COMMAND mkdir -p ${_wrapper_dir} - COMMAND echo ${_functions_include} > "${_wrapper_path}" + COMMAND ${CMAKE_COMMAND} -E make_directory ${_wrapper_dir} + COMMAND ${CMAKE_COMMAND} -E echo ${_functions_include} > "${_wrapper_path}" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/Functions.h" "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/NativeFunctions.h" @@ -44,7 +44,7 @@ foreach(kernel ${_kernels}) add_custom_command( OUTPUT "${_wrapper_dir}/supported_features.cpp" "${_wrapper_dir}/supported_features.h" - COMMAND mkdir -p ${_wrapper_dir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${_wrapper_dir} COMMAND ${PYTHON_EXECUTABLE} kernels/test/gen_supported_features.py kernels/${_supported_features_kernel}/test/supported_features_def.yaml > @@ -73,17 +73,35 @@ foreach(kernel ${_kernels}) "${CMAKE_CURRENT_BINARY_DIR}/../../kernels/${kernel}/${kernel}_ops_lib" ) endif() + + # Copy with glob needs to be handle in a platform-specific manner. + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # The quoting here is complicated, because there are three levels of + # interpretation: CMake -> Batch -> Powershell. The invoked (batch) command + # should look like `powershell -Command "Copy-Item ... -Path \"...\" ...". + # Powershell sees `Copy-Item -Path "..." ...`. + set(_copy_headers_cmd + powershell + -Command + "Copy-Item -Path \\\"${_kernel_ops_lib_path}/*.h\\\" -Destination \\\"${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/\\\"" + ) + else() + set(_copy_headers_cmd + cp + "${_kernel_ops_lib_path}/*.h" + "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/" + ) + endif() + add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/Functions.h" "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/NativeFunctions.h" "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/RegisterKernels.h" COMMAND - mkdir -p - "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/" - COMMAND - cp "${_kernel_ops_lib_path}/*.h" + ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/include/${kernel}/executorch/kernels/${kernel}/" + COMMAND ${_copy_headers_cmd} DEPENDS ${_kernel_ops_lib} ) endforeach() diff --git a/kernels/test/export_test_model.ps1 b/kernels/test/export_test_model.ps1 new file mode 100644 index 00000000000..d19e2a713d9 --- /dev/null +++ b/kernels/test/export_test_model.ps1 @@ -0,0 +1,24 @@ +param ( + [string]$Modules, + [string]$OutDir, + [string]$CondaEnv +) + +Set-PSDebug -Trace 1 + +# Activate the VS dev environment - needed for dynamo. Try to use vswhere to locate the install. If not, +# fall back to a reasonable guess for the build tools, which also happens to match the CLI setup. +$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" +if (Test-Path $vswherePath) { + $vsInstallPath = & $vswherePath -latest -property installationPath +} else { + $vsInstallPath = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\" +} + +& "$vsInstallPath\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation + +conda activate $CondaEnv + +$Modules = $Modules.Replace(" ", ",") +echo "Modules: $Modules" +python -m test.models.export_program --modules "$Modules" --outdir "$OutDir" diff --git a/pytest-windows.ini b/pytest-windows.ini index 0eb30e3583d..0959318afdd 100644 --- a/pytest-windows.ini +++ b/pytest-windows.ini @@ -100,6 +100,7 @@ addopts = #extension/llm/export --deselect=extension/pybindings/test/test_pybindings.py::PybindingsTest::test_method_quantized_ops --deselect=extension/pybindings/test/test_pybindings.py::PybindingsTest::test_quantized_ops + --deselect=extension/pybindings/test/test_pybindings.py::PybindingsTest::test_program_data_separation --deselect=runtime/test/test_runtime.py::RuntimeTest::test_load_program_with_path --deselect=exir/backend/test/test_compatibility.py::TestCompatibility::test_compatibility_in_runtime --deselect=exir/backend/test/test_compatibility.py::TestCompatibility::test_compatibility_in_runtime_edge_program_manager @@ -108,6 +109,7 @@ addopts = --deselect=extension/llm/custom_ops/test_sdpa_with_kv_cache.py::SDPATestForSpeculativeDecode::test_sdpa_with_cache_seq_len_130 --deselect=devtools/inspector/tests/inspector_test.py::TestInspector::test_etrecord_populates_correct_edge_dialect_aot_intermediate_outputs --deselect=devtools/inspector/tests/inspector_test.py::TestInspector::test_etrecord_populates_correct_export_program_aot_intermediate_outputs + --deselect=runtime/test/test_runtime_etdump_gen.py::RuntimeETDumpGenTest::test_etdump_generation # run the same tests multiple times to determine their # flakiness status. Default to 50 re-runs diff --git a/runtime/core/exec_aten/testing_util/tensor_util.cpp b/runtime/core/exec_aten/testing_util/tensor_util.cpp index 35ddbe8ac15..058f268ba68 100644 --- a/runtime/core/exec_aten/testing_util/tensor_util.cpp +++ b/runtime/core/exec_aten/testing_util/tensor_util.cpp @@ -28,6 +28,43 @@ namespace testing { namespace { +/** + * Returns true if the two elements are close according to the description on + * `tensors_are_close()`. + * + * T must be a floating point type. Non-floating point data should be compared + * directly. + */ +template +bool element_is_close(const T a, const T b, double rtol, double atol) { + if constexpr (is_reduced_floating_point_v) { + // MSVC complains about ambiguous overloads, so explicitly cast to float to + // compare. + return element_is_close( + static_cast(a), static_cast(b), rtol, atol); + } else { + if (std::isnan(a) && std::isnan(b)) { + // NaN == NaN + } else if (!std::isfinite(a) && !std::isfinite(b) && ((a > 0) == (b > 0))) { + // -Inf == -Inf + // +Inf == +Inf + } else if (rtol == 0 && atol == 0) { + // Exact comparison; avoid unnecessary math. + if (a != b) { + return false; + } + } else { + auto allowed_error = atol + std::abs(rtol * b); + auto actual_error = std::abs(a - b); + if (!std::isfinite(actual_error) || actual_error > allowed_error) { + return false; + } + } + + return true; + } +} + /** * Returns true if the two arrays are close according to the description on * `tensors_are_close()`. @@ -55,23 +92,8 @@ bool data_is_close( const auto ai = a[i]; const auto bi = b[i]; - if (std::isnan(ai) && std::isnan(bi)) { - // NaN == NaN - } else if ( - !std::isfinite(ai) && !std::isfinite(bi) && ((ai > 0) == (bi > 0))) { - // -Inf == -Inf - // +Inf == +Inf - } else if (rtol == 0 && atol == 0) { - // Exact comparison; avoid unnecessary math. - if (ai != bi) { - return false; - } - } else { - auto allowed_error = atol + std::abs(rtol * bi); - auto actual_error = std::abs(ai - bi); - if (!std::isfinite(actual_error) || actual_error > allowed_error) { - return false; - } + if (!element_is_close(ai, bi, rtol, atol)) { + return false; } } return true; diff --git a/runtime/executor/test/CMakeLists.txt b/runtime/executor/test/CMakeLists.txt index 12d04c382ad..05d149ab1b4 100644 --- a/runtime/executor/test/CMakeLists.txt +++ b/runtime/executor/test/CMakeLists.txt @@ -17,6 +17,31 @@ set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include(${EXECUTORCH_ROOT}/tools/cmake/Test.cmake) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # Use a wrapper script to set up the environment for MSVC to make Dynamo + # export work. + set(_export_program_cmd + powershell + ${EXECUTORCH_ROOT}/kernels/test/export_test_model.ps1 + -Modules + "\"ModuleAdd,ModuleAddHalf,ModuleAddMul,ModuleDynamicCatUnallocatedIO,ModuleIndex,ModuleMultipleEntry,ModuleSimpleTrain,ModuleStateful\"" + -outDir + "${CMAKE_CURRENT_BINARY_DIR}" + -CondaEnv + $ENV{CONDA_DEFAULT_ENV} + ) +else() + set(_export_program_cmd + ${PYTHON_EXECUTABLE} + -m + test.models.export_program + --modules + "ModuleAdd,ModuleAddHalf,ModuleAddMul,ModuleDynamicCatUnallocatedIO,ModuleIndex,ModuleMultipleEntry,ModuleSimpleTrain,ModuleStateful" + --outdir + "${CMAKE_CURRENT_BINARY_DIR}" + ) +endif() + add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ModuleAdd.pte" "${CMAKE_CURRENT_BINARY_DIR}/ModuleAddHalf.pte" @@ -29,10 +54,7 @@ add_custom_command( "${CMAKE_CURRENT_BINARY_DIR}/ModuleSimpleTrain.pte" "${CMAKE_CURRENT_BINARY_DIR}/ModuleStateful.pte" "${CMAKE_CURRENT_BINARY_DIR}/delegated/ModuleAddMul.pte" - COMMAND - ${PYTHON_EXECUTABLE} -m test.models.export_program --modules - "ModuleAdd,ModuleAddHalf,ModuleAddMul,ModuleDynamicCatUnallocatedIO,ModuleIndex,ModuleMultipleEntry,ModuleSimpleTrain,ModuleStateful" - --outdir "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND ${_export_program_cmd} COMMAND ${PYTHON_EXECUTABLE} -m test.models.export_program --modules "ModuleAddMul" --external-constants --outdir "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/runtime/kernel/test/CMakeLists.txt b/runtime/kernel/test/CMakeLists.txt index c70ec5d135b..a8166017e53 100644 --- a/runtime/kernel/test/CMakeLists.txt +++ b/runtime/kernel/test/CMakeLists.txt @@ -39,12 +39,6 @@ add_test(kernel_runtime_context_test kernel_runtime_context_test) add_executable( operator_registry_max_kernel_num_test operator_registry_max_kernel_num_test.cpp - ../operator_registry.cpp - ../../core/evalue.cpp - ../../platform/abort.cpp - ../../platform/log.cpp - ../../platform/runtime.cpp - ../../platform/default/posix.cpp ) target_link_libraries( operator_registry_max_kernel_num_test GTest::gtest GTest::gtest_main