diff --git a/.ci/scripts/tests/test_filter_cuda_matrix.py b/.ci/scripts/tests/test_filter_cuda_matrix.py index 87cace023e4..33ba959a4f1 100644 --- a/.ci/scripts/tests/test_filter_cuda_matrix.py +++ b/.ci/scripts/tests/test_filter_cuda_matrix.py @@ -327,6 +327,8 @@ def test_supported_toolkits_select_the_matching_torch_index(self): INSTALL_UTILS.platform, "system", return_value="Linux" ), mock.patch.object( INSTALL_UTILS.subprocess, "run", return_value=detected + ), mock.patch.dict( + os.environ, {"CMAKE_ARGS": "-DEXECUTORCH_BUILD_CUDA=ON"} ): self.assertEqual( INSTALL_UTILS.determine_torch_url(base_url), diff --git a/.ci/scripts/wheel/test_shared_libraries.py b/.ci/scripts/wheel/test_shared_libraries.py index a1d8c791b83..55120809626 100644 --- a/.ci/scripts/wheel/test_shared_libraries.py +++ b/.ci/scripts/wheel/test_shared_libraries.py @@ -92,6 +92,7 @@ _AOTI_SHIM_SYMBOLS = ( "aoti_torch_empty_strided", "aoti_torch_delete_tensor_object", + "aoti_torch_dtype_element_size", "executorch::backends::cuda::CUDAStreamGuard::create", # From the CUDA sources rather than the C++ ones. The build drops every .cu file when no working # compiler is found, and the library is still produced from its .cpp sources, so a check that diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml index dca55ee5d24..29e9c2cd339 100644 --- a/.github/workflows/pull.yml +++ b/.github/workflows/pull.yml @@ -213,7 +213,13 @@ jobs: EXECUTORCH_PYBIND_TEST_BFLOAT16_CHANNELS_LAST_PTE=cmake-out-no-aten/add_bfloat16_channels_last.pte \ PYTHONPATH=cmake-out-no-aten:.. \ python extension/pybindings/test/test_pybindings_no_aten.py - if ldd cmake-out-no-aten/_C*.so | grep -Eiq 'torch|aten|c10'; then + NO_ATEN_LINKS="$(ldd cmake-out-no-aten/_C*.so)" + printf '%s\n' "$NO_ATEN_LINKS" + if grep -q 'not found' <<<"$NO_ATEN_LINKS"; then + echo "torch-free pybindings have a missing runtime dependency" + exit 1 + fi + if grep -Eiq '^[[:space:]]*(lib(torch|aten|c10)[^[:space:]/]*\.so|[^[:space:]]*/lib(torch|aten|c10)[^[:space:]/]*\.so)' <<<"$NO_ATEN_LINKS"; then echo "torch-free pybindings unexpectedly link against ATen" exit 1 fi diff --git a/.github/workflows/release-runner-groups-refresh.yml b/.github/workflows/release-runner-groups-refresh.yml index 59cd8871899..a7631683abd 100644 --- a/.github/workflows/release-runner-groups-refresh.yml +++ b/.github/workflows/release-runner-groups-refresh.yml @@ -23,8 +23,20 @@ concurrency: cancel-in-progress: true jobs: + dispatch: + if: github.repository_owner == 'pytorch' && github.event_name == 'push' + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Refresh runner groups from protected main + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: gh workflow run release-runner-groups-refresh.yml --ref main -f apply=true + reconcile: - if: github.repository_owner == 'pytorch' + if: github.repository_owner == 'pytorch' && github.event_name != 'push' runs-on: ubuntu-latest environment: ${{ github.event_name != 'pull_request' && 'runner-group' || '' }} steps: diff --git a/backends/aoti/common_shims_slim.cpp b/backends/aoti/common_shims_slim.cpp index d9e255bf220..fa94c55455f 100644 --- a/backends/aoti/common_shims_slim.cpp +++ b/backends/aoti/common_shims_slim.cpp @@ -164,6 +164,11 @@ int32_t aoti_torch_dtype_bool() { return 11; // ScalarType::Bool } +size_t aoti_torch_dtype_element_size(int32_t dtype) { + using namespace executorch::backends::aoti::slim::c10; + return elementSize(static_cast(dtype)); +} + // ============================================================ // Device Type Constants - Implementations // ============================================================ diff --git a/backends/aoti/common_shims_slim.h b/backends/aoti/common_shims_slim.h index 0d60f578605..29b7fa9b3d7 100644 --- a/backends/aoti/common_shims_slim.h +++ b/backends/aoti/common_shims_slim.h @@ -87,6 +87,8 @@ AOTI_SHIM_EXPORT int32_t aoti_torch_dtype_int8(); AOTI_SHIM_EXPORT int32_t aoti_torch_dtype_uint8(); AOTI_SHIM_EXPORT int32_t aoti_torch_dtype_bool(); +AOTI_SHIM_EXPORT size_t aoti_torch_dtype_element_size(int32_t dtype); + // ============================================================ // Device Type Constants - Declarations // ============================================================ diff --git a/backends/cuda/runtime/aoti_cuda_shims.lib b/backends/cuda/runtime/aoti_cuda_shims.lib index c0d61c61100..9a8acd416c5 100644 Binary files a/backends/cuda/runtime/aoti_cuda_shims.lib and b/backends/cuda/runtime/aoti_cuda_shims.lib differ diff --git a/backends/cuda/runtime/aoti_cuda_shims.lib.md b/backends/cuda/runtime/aoti_cuda_shims.lib.md index af2fb65ca13..39458f379de 100644 --- a/backends/cuda/runtime/aoti_cuda_shims.lib.md +++ b/backends/cuda/runtime/aoti_cuda_shims.lib.md @@ -10,12 +10,12 @@ pin moves and the generated wrapper starts calling something new: ``` nm --defined-only aoti_cuda_shims.lib \ - | sed -n 's/.* T _\?\(aoti_torch_[a-z_0-9]*\)$/\1/p' \ + | sed -n 's/.* T _\?\(aoti_torch_[A-Za-z_0-9]*\)$/\1/p' \ | sort -u > exports.txt # add the new names to exports.txt, then { echo 'LIBRARY aoti_cuda_shims.dll'; echo 'EXPORTS'; sed 's/^/ /' exports.txt; } \ > aoti_cuda_shims.def -x86_64-w64-mingw32-dlltool -d aoti_cuda_shims.def -l aoti_cuda_shims.lib \ +x86_64-w64-mingw32-dlltool -m i386:x86-64 -d aoti_cuda_shims.def -l aoti_cuda_shims.lib \ --dllname aoti_cuda_shims.dll # zero the archive metadata so the file is reproducible mkdir extract && cd extract && x86_64-w64-mingw32-ar x ../aoti_cuda_shims.lib \ @@ -27,6 +27,9 @@ The archive has to be built at a fresh path. Updating it in place keeps the reve member order the generator produced, so the same export list would not give the same bytes twice. +Use the relative output name shown above in each build directory. The generator embeds +the output name in its object names, so absolute temporary paths change the bytes. + Then check the result is a superset of what it replaced and that no member carries a timestamp, since this file ships in the wheel and a stamped one makes two builds of the same source differ. diff --git a/backends/cuda/tests/test_int6_large_quantized_gemm.py b/backends/cuda/tests/test_int6_large_quantized_gemm.py index bb762b24e0c..ff8e96c6e8e 100644 --- a/backends/cuda/tests/test_int6_large_quantized_gemm.py +++ b/backends/cuda/tests/test_int6_large_quantized_gemm.py @@ -76,8 +76,9 @@ def test_buckets_and_generic_candidate_counts(self): for bucket, expected in expected_counts.items(): configs = tile_autotune_configs(bucket) self.assertEqual(len(configs), expected) - self.assertEqual(len(legal_configs(bucket, 256)), expected // 3) - self.assertEqual(len(legal_configs(bucket, 5376)), expected) + legal_expected = 18 if torch.version.hip is not None else expected + self.assertEqual(len(legal_configs(bucket, 256)), legal_expected // 3) + self.assertEqual(len(legal_configs(bucket, 5376)), legal_expected) self.assertTrue(hasattr(torch.ops.triton, f"int6_quantized_gemm_m{bucket}")) @unittest.skipUnless(torch.cuda.is_available(), "CUDA required") diff --git a/backends/cuda/triton/kernels/int6_quantized_gemm.py b/backends/cuda/triton/kernels/int6_quantized_gemm.py index 5ddd6fe8ed3..bf170e56570 100644 --- a/backends/cuda/triton/kernels/int6_quantized_gemm.py +++ b/backends/cuda/triton/kernels/int6_quantized_gemm.py @@ -1239,6 +1239,10 @@ def _tile_main_loop_trips(args) -> int: def _prune_tiles(configs, named_args, **kwargs): + if torch.version.hip is not None: + # Larger M tiles can access invalid memory on masked tails with ROCm + # 7.14. Keep the eight-row path for the release. + configs = [config for config in configs if config.kwargs["BLOCK_M"] == 8] return prune_by_main_loop_trips( _tile_main_loop_trips, configs, named_args, **kwargs ) diff --git a/examples/cuda/scripts/export_amd_pointwise.py b/examples/cuda/scripts/export_amd_pointwise.py index 67c07da7a0e..d701a29e722 100644 --- a/examples/cuda/scripts/export_amd_pointwise.py +++ b/examples/cuda/scripts/export_amd_pointwise.py @@ -66,7 +66,11 @@ def main() -> None: CompileSpec("target_device", f"cuda:{device_index}".encode()), CompileSpec("triton_kernel_mode", b"ON"), CompileSpec("max_autotune", b"OFF"), - CompileSpec("autotune_at_compile_time", b"OFF"), + # Compile the Triton kernels during AOTI lowering: with + # autotune_at_compile_time OFF, AOTInductor also generates JIT code + # and the JIT code fails to run on gfx950 with the pinned + # ROCm 7.14 toolchain, so lowering never completes. + CompileSpec("autotune_at_compile_time", b"ON"), ] edge_program = to_edge_transform_and_lower( exported_program, @@ -106,10 +110,20 @@ def main() -> None: if not ptd_path.is_file(): raise RuntimeError(f"expected AOTI data file was not created: {ptd_path}") - # Verify the embedded code object matches the active device. - triple = f"amdgcn-amd-amdhsa--{arch}".encode() - if triple not in pte_path.read_bytes(): + # Verify the generated code object targets the active device. The + # toolchain embeds the bare architecture name, not the full amdgcn + # triple, in the program and in the hsaco files, so check for that + # and require a compiled hsaco for the device. + arch_marker = arch.encode() + if arch_marker not in pte_path.read_bytes(): raise RuntimeError(f"{pte_path} embeds no code object for {arch}") + hsaco_paths = [ + hsaco_path + for hsaco_path in cache_dir.rglob("*.hsaco") + if arch_marker in hsaco_path.read_bytes() + ] + if not hsaco_paths: + raise RuntimeError(f"no hsaco code object for {arch} under {cache_dir}") print(f"ROCm: {torch.version.hip}") print(f"Compile GPU: cuda:{device_index} {device.name} ({device.gcnArchName})") @@ -119,6 +133,9 @@ def main() -> None: print("Triton sources:") for source_path in triton_sources: print(f" {source_path}") + print("Code objects:") + for hsaco_path in hsaco_paths: + print(f" {hsaco_path}") if __name__ == "__main__":