From 2cfc7190a2b84a5cf8da6be33da29ed4f2d1aa1d Mon Sep 17 00:00:00 2001 From: XYenChi Date: Sun, 19 Apr 2026 23:32:28 +0800 Subject: [PATCH 01/16] Add RISC-V Blocklist (#1) * Add RISC-V 64 BLOCK_LIST * Skip long time testcase --- test/run_test.py | 47 +++++++++++++++++++++++++++++++++++++++++++++ test/test_linalg.py | 1 + third_party/kineto | 2 +- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/test/run_test.py b/test/run_test.py index 9a85ecd2195cd..c753d18ab7c6c 100755 --- a/test/run_test.py +++ b/test/run_test.py @@ -110,6 +110,7 @@ def upload_adhoc_failure_json(*args, **kwargs): INDUCTOR_TEST_PREFIX = "inductor" IS_SLOW = "slow" in TEST_CONFIG or "slow" in BUILD_ENVIRONMENT IS_S390X = platform.machine() == "s390x" +IS_RISCV64 = platform.machine() == "riscv64" # Note [ROCm parallel CI testing] @@ -285,6 +286,45 @@ def __contains__(self, item): "test_xpu", ] +RISCV64_BLOCKLIST = [ + # disable distributed related test + "inductor/test_distributed_patterns" + "fx/test_dce_pass" + "export/test_cpp_serdes" + "export/test_export" + "export/test_export_strict" + "export/test_export_training_ir_to_run_decomp" + "export/test_retraceability" + "export/test_serdes" + "export/test_strict_export_v2" + "test_public_bindings" + # quantized engine NoQEngine is not supported + "test_torch" + "ao/sparsity/test_composability" + # QNNPACK is not supported + "export/test_converter" + # record_contex_cpp is not support on non-linux non-x86_64 platforms + "torch_np/numpy_tests/core/test_numeric" + # Failed to import torch.distributed.run: cannot import name 'Store' from 'torch.distributed' + "test_testing" + # TODO:L1 cache size = 0, need to fix + "inductor/test_cpu_select_algorithm" + "inductor/test_aot_inductor_arrayref" + "inductor/test_cpu_repro" + # TODO:scalar value not equal, need to fix + "profiler/test_profiler" + # TODO precision + "test_binary_ufuncs" + "test_decomp" + # TODO no CUDA related module + "quantization/core/test_workflow_module" # TestFakeQuantize.test_fq_module_per_channel + "quantization/core/test_workflow_ops" + "quantization/core/test_quantized_op" + # z3-solver build fail + "test_proxy_tensor" +] + + # The tests inside these files should never be run in parallel with each other RUN_PARALLEL_BLOCKLIST = [ "test_extension_utils", @@ -1875,6 +1915,13 @@ def get_selected_tests(options) -> list[str]: selected_tests, "Skip distributed tests on s390x", ) + elif IS_RISCV64: + selected_tests = exclude_tests(RISCV64_BLOCKLIST, selected_tests, "on riscv64") + selected_tests = exclude_tests( + DISTRIBUTED_TESTS, + selected_tests, + "Skip distributed tests on riscv64", + ) # skip all distributed tests if distributed package is not available. if not dist.is_available(): diff --git a/test/test_linalg.py b/test/test_linalg.py index 0ac35e077946d..5e7018e1cf9ab 100644 --- a/test/test_linalg.py +++ b/test/test_linalg.py @@ -9002,6 +9002,7 @@ def test_matrix_exp_backward_input_validation(self, device, dtype): with self.assertRaisesRegex(RuntimeError, "must be batches of square matrices"): torch.ops.aten.matrix_exp_backward(non_square, grad_non_square) + @slowTest @skipCUDAIfNoMagmaAndNoLinalgsolver @skipCPUIfNoLapack @dtypes(torch.float, torch.double, torch.complex64, torch.complex128) diff --git a/third_party/kineto b/third_party/kineto index 5902263f641a8..7739225509b84 160000 --- a/third_party/kineto +++ b/third_party/kineto @@ -1 +1 @@ -Subproject commit 5902263f641a8634af077031fb4befc7162cefbb +Subproject commit 7739225509b847e7b1ce7638f1ead383d15b077f From bf9cf6d4f77057f64d6bbf721b9d31056b913f6e Mon Sep 17 00:00:00 2001 From: Bo YU Date: Wed, 22 Apr 2026 08:10:52 +0000 Subject: [PATCH 02/16] add riscv64 ci --- .github/workflows/ci-riscv64.yml | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 .github/workflows/ci-riscv64.yml diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml new file mode 100644 index 0000000000000..ffc859265b3c9 --- /dev/null +++ b/.github/workflows/ci-riscv64.yml @@ -0,0 +1,99 @@ +# Note: this runner is provided externally, so we minimize its access to +# secrets. +on: + push: + branches: [riscv] + + pull_request_target: + types: [opened, synchronize, reopened] + + +name: CI (riscv64) + +permissions: + contents: read + # No permissions to secrets. + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +# FIXME: Drop this +env: + RUSTFLAGS: -D warnings + CARGO_TERM_COLOR: always + +jobs: + build: + name: Build and test + runs-on: [self-hosted, linux, amd64] + # This is in its own separate environment. + environment: riscv64 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 # merge-base + + - name: Extract PR info + run: | + echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV + echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> GITHUB_ENV + echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> GITHUB_ENV + + - name: Diff base and head + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "Push PR build" + BASE_REF="${{ github.base_ref }}" + HEAD_REF="${{ github.head_ref }}" + + echo "Base ref: $BASE_REF" + echo "Head ref: $HEAD_REF" + + # 强约束: PR 必须基于 riscv + if [ "$BASE_REF" != "riscv" ]; then + echo "ERROR: PR must target 'riscv' branch, got '$BASE_REF'" + exit 1 + fi + + BASE="$BASE_SHA" + HEAD="$HEAD_SHA" + else + echo "Push to riscv" + # 统一用 riscv 作为 baseline + git fetch origin riscv + + BASE=$(git merge-base HEAD origin/main) + HEAD=$(git rev-parse HEAD) + + fi + + echo "BASE_COMMIT=$BASE" >> $GITHUB_ENV + echo "HEAD_COMMIT=$HEAD" >> $GITHUB_ENV + + echo "Base: $BASE" + echo "Head: $HEAD" + + - name: Generate patch + run: | + echo "Generating patch..." + + git diff $BASE_COMMIT $HEAD_COMMIT > patch.diff + + echo "Patch size:" + wc -l patch.diff + cat patch.diff + + # 可选:避免空 patch + if [ ! -s patch.diff ]; then + echo "Warning: empty patch" + fi + + - name: Trigger Jenkins Job + run: | + export BASE_COMMIT=${BASE_COMMIT} + export PATCH_FILE=$(pwd)/patch.diff + export GITHUB_PR=${PR_NUMBER:-0} + + #bash /home/jenkins/scripts/jenkins-run.sh From 6c3b1f17c77613e59e630139d849d193e63ca8e9 Mon Sep 17 00:00:00 2001 From: vimer Date: Fri, 24 Apr 2026 14:58:31 +0800 Subject: [PATCH 03/16] Test ci with PR (#8) * Add riscv64 ci with PR --- .github/workflows/ci-riscv64.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index ffc859265b3c9..c14ce1e4c3efa 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -6,7 +6,6 @@ on: pull_request_target: types: [opened, synchronize, reopened] - name: CI (riscv64) @@ -33,7 +32,7 @@ jobs: - name: Checkout repo uses: actions/checkout@v4 with: - fetch-depth: 0 # merge-base + fetch-depth: 3000 # shadow clone? - name: Extract PR info run: | @@ -44,7 +43,7 @@ jobs: - name: Diff base and head run: | if [ "${{ github.event_name }}" = "pull_request" ]; then - echo "Push PR build" + echo "Push PR build" BASE_REF="${{ github.base_ref }}" HEAD_REF="${{ github.head_ref }}" @@ -57,8 +56,12 @@ jobs: exit 1 fi - BASE="$BASE_SHA" - HEAD="$HEAD_SHA" + // need to get contents of the PR + git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-head + git fetch origin pull/${{ github.event.pull_request.number }}/base:pr-base + + BASE=$(git merge-base pr-base pr-head) + HEAD=pr-head else echo "Push to riscv" # 统一用 riscv 作为 baseline From ff108da766ac4fc65e3b1ebb7ea97dc5b3a3eb34 Mon Sep 17 00:00:00 2001 From: Bo YU Date: Fri, 24 Apr 2026 07:06:46 +0000 Subject: [PATCH 04/16] Fix no main brach issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⭐ Run Main Diff base and head Push to riscv From https://github.com/RuyiAI-Stack/pytorch * branch riscv -> FETCH_HEAD fatal: Not a valid object name origin/main Error: ❌ Failure - Main Diff base and head Error: exit status 128 --- .github/workflows/ci-riscv64.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index c14ce1e4c3efa..3b3ddd23483e7 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -33,16 +33,16 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 3000 # shadow clone? + ref: ${{ github.sha }} # including latest sha - name: Extract PR info run: | echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> GITHUB_ENV - echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> GITHUB_ENV - + - name: Diff base and head run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then + if [[ "${{ github.event_name }}" = "pull_request" || "${{ github.event_name }}" == "pull_request_target" ]]; then echo "Push PR build" BASE_REF="${{ github.base_ref }}" HEAD_REF="${{ github.head_ref }}" @@ -56,19 +56,19 @@ jobs: exit 1 fi - // need to get contents of the PR + # need to get contents of the PR git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-head - git fetch origin pull/${{ github.event.pull_request.number }}/base:pr-base - - BASE=$(git merge-base pr-base pr-head) + git fetch origin main + BASE=$(git merge-base pr-head origin/main) HEAD=pr-head else echo "Push to riscv" # 统一用 riscv 作为 baseline - git fetch origin riscv + git fetch origin main + #git fetch origin riscv - BASE=$(git merge-base HEAD origin/main) - HEAD=$(git rev-parse HEAD) + BASE=$(git merge-base ${{ github.sha }} origin/main) # The latest commit + HEAD=${{ github.sha }} fi @@ -97,6 +97,5 @@ jobs: run: | export BASE_COMMIT=${BASE_COMMIT} export PATCH_FILE=$(pwd)/patch.diff - export GITHUB_PR=${PR_NUMBER:-0} - #bash /home/jenkins/scripts/jenkins-run.sh + bash /home/jenkins/scripts/jenkins-run.sh From 47c19e55efd0e575563bd56133031f18a3ab1777 Mon Sep 17 00:00:00 2001 From: Bo YU Date: Sat, 25 Apr 2026 14:21:06 +0000 Subject: [PATCH 05/16] move the patch to dest --- .github/workflows/ci-riscv64.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index 3b3ddd23483e7..dec68a6d12c16 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -39,7 +39,7 @@ jobs: run: | echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> GITHUB_ENV - + - name: Diff base and head run: | if [[ "${{ github.event_name }}" = "pull_request" || "${{ github.event_name }}" == "pull_request_target" ]]; then @@ -82,20 +82,20 @@ jobs: run: | echo "Generating patch..." - git diff $BASE_COMMIT $HEAD_COMMIT > patch.diff + SHORT_HEAD=${HEAD_COMMIT:0:7} + PATCH_NAME="patch_${SHORT_HEAD}.patch" + + git diff $BASE_COMMIT $HEAD_COMMIT > $PATCH_NAME echo "Patch size:" - wc -l patch.diff - cat patch.diff + wc -l $PATCH_NAME - # 可选:避免空 patch - if [ ! -s patch.diff ]; then - echo "Warning: empty patch" - fi + cp $PATCH_NAME /home/jenkins/patch/ + cat /home/jenkins/patch/$PATCH_NAME - name: Trigger Jenkins Job run: | export BASE_COMMIT=${BASE_COMMIT} export PATCH_FILE=$(pwd)/patch.diff - bash /home/jenkins/scripts/jenkins-run.sh + #bash /home/jenkins/scripts/jenkins-run.sh From b984cb258fc01ec8e2c4b0da45708c9bd0d9f307 Mon Sep 17 00:00:00 2001 From: XYenChi Date: Mon, 27 Apr 2026 10:59:07 +0800 Subject: [PATCH 06/16] Fix block list format and remove test_cpu_select_algorithm (#4) * mklnn is unavailable on RISC-V * Remove test_cpu_select_algorithm from block_list * Fix block list format --- test/inductor/test_cpu_select_algorithm.py | 2 + test/run_test.py | 52 +++++++++++----------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/test/inductor/test_cpu_select_algorithm.py b/test/inductor/test_cpu_select_algorithm.py index 236712f7950bb..8c064db44220e 100644 --- a/test/inductor/test_cpu_select_algorithm.py +++ b/test/inductor/test_cpu_select_algorithm.py @@ -1583,6 +1583,7 @@ def forward(self, x): vec_amx = VecAMX() self._check_amx_counter(vec_amx) + @unittest.skipIf(not torch._C._has_mkldnn, "MKLDNN is not enabled") @inductor_config.patch({"freezing": True}) @patches @torch.no_grad @@ -1700,6 +1701,7 @@ def forward(self, x, scale): vec_amx = VecAMX() self._check_amx_counter(vec_amx) + @unittest.skipIf(not torch._C._has_mkldnn, "MKLDNN is not enabled") @inductor_config.patch({"freezing": True, "cpp.enable_concat_linear": True}) @patches @torch.no_grad diff --git a/test/run_test.py b/test/run_test.py index c753d18ab7c6c..4939b685e0163 100755 --- a/test/run_test.py +++ b/test/run_test.py @@ -288,40 +288,40 @@ def __contains__(self, item): RISCV64_BLOCKLIST = [ # disable distributed related test - "inductor/test_distributed_patterns" - "fx/test_dce_pass" - "export/test_cpp_serdes" - "export/test_export" - "export/test_export_strict" - "export/test_export_training_ir_to_run_decomp" - "export/test_retraceability" - "export/test_serdes" - "export/test_strict_export_v2" - "test_public_bindings" + "inductor/test_distributed_patterns", + "fx/test_dce_pass", + "export/test_cpp_serdes", + "export/test_export", + "export/test_export_strict", + "export/test_export_training_ir_to_run_decomp", + "export/test_retraceability", + "export/test_serdes", + "export/test_strict_export_v2", + "test_public_bindings", # quantized engine NoQEngine is not supported - "test_torch" - "ao/sparsity/test_composability" + "test_torch", + "ao/sparsity/test_composability", # QNNPACK is not supported - "export/test_converter" + "export/test_converter", # record_contex_cpp is not support on non-linux non-x86_64 platforms - "torch_np/numpy_tests/core/test_numeric" + "torch_np/numpy_tests/core/test_numeric", # Failed to import torch.distributed.run: cannot import name 'Store' from 'torch.distributed' - "test_testing" - # TODO:L1 cache size = 0, need to fix - "inductor/test_cpu_select_algorithm" - "inductor/test_aot_inductor_arrayref" - "inductor/test_cpu_repro" + "test_testing", + "inductor/test_aot_inductor_arrayref", + "inductor/test_cpu_repro", + # TODO: mkldnn not available, shape guard failures on RISC-V + "inductor/test_cpu_select_algorithm", # TODO:scalar value not equal, need to fix - "profiler/test_profiler" + "profiler/test_profiler", # TODO precision - "test_binary_ufuncs" - "test_decomp" + "test_binary_ufuncs", + "test_decomp", # TODO no CUDA related module - "quantization/core/test_workflow_module" # TestFakeQuantize.test_fq_module_per_channel - "quantization/core/test_workflow_ops" - "quantization/core/test_quantized_op" + "quantization/core/test_workflow_module", # TestFakeQuantize.test_fq_module_per_channel + "quantization/core/test_workflow_ops", + "quantization/core/test_quantized_op", # z3-solver build fail - "test_proxy_tensor" + "test_proxy_tensor", ] From f7e97ba3b6971ed3d66789352759333d106efcb3 Mon Sep 17 00:00:00 2001 From: Bo YU Date: Sat, 25 Apr 2026 14:21:06 +0000 Subject: [PATCH 07/16] move the patch to dest --- .github/workflows/ci-riscv64.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index dec68a6d12c16..d8cd1ecdd8de3 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -57,14 +57,14 @@ jobs: fi # need to get contents of the PR - git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-head - git fetch origin main + git fetch --quiet origin pull/${{ github.event.pull_request.number }}/head:pr-head + git fetch --quiet origin main BASE=$(git merge-base pr-head origin/main) HEAD=pr-head else echo "Push to riscv" # 统一用 riscv 作为 baseline - git fetch origin main + git fetch --quiet origin main #git fetch origin riscv BASE=$(git merge-base ${{ github.sha }} origin/main) # The latest commit @@ -93,9 +93,8 @@ jobs: cp $PATCH_NAME /home/jenkins/patch/ cat /home/jenkins/patch/$PATCH_NAME + echo "PATCH_FILE=$PATCH_NAME" >> $GITHUB_ENV + - name: Trigger Jenkins Job run: | - export BASE_COMMIT=${BASE_COMMIT} - export PATCH_FILE=$(pwd)/patch.diff - - #bash /home/jenkins/scripts/jenkins-run.sh + bash /home/jenkins/scripts/jenkins-run.sh $BASE_COMMIT $PATCH_FILE From 89901fb309dd78f6c8a09ccdc176f701e3653bce Mon Sep 17 00:00:00 2001 From: vimer Date: Sun, 3 May 2026 09:05:59 +0800 Subject: [PATCH 08/16] [blacklist]: update it (#11) These cases are too slow on riscv64, adding them to here simply Drop test_torch from the list because it is one core case --- test/run_test.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/run_test.py b/test/run_test.py index 4939b685e0163..22561c879d13d 100755 --- a/test/run_test.py +++ b/test/run_test.py @@ -298,8 +298,6 @@ def __contains__(self, item): "export/test_serdes", "export/test_strict_export_v2", "test_public_bindings", - # quantized engine NoQEngine is not supported - "test_torch", "ao/sparsity/test_composability", # QNNPACK is not supported "export/test_converter", @@ -322,6 +320,15 @@ def __contains__(self, item): "quantization/core/test_quantized_op", # z3-solver build fail "test_proxy_tensor", + # too slow on riscv64 + # 53013.55 s + "functorch/test_aotdispatch", + # 25069 s + "functorch/test_ops", + # 17528 s + "test_transformers", + # 10897 s + "functorch/test_vmap", ] From af405abae3ab26218aaf3111f1f9835dcabf7a5d Mon Sep 17 00:00:00 2001 From: vimer Date: Thu, 7 May 2026 12:32:52 +0800 Subject: [PATCH 09/16] Use commit sha on PR workflow (#12) --- .github/workflows/ci-riscv64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index d8cd1ecdd8de3..44cd575a01e01 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -60,7 +60,7 @@ jobs: git fetch --quiet origin pull/${{ github.event.pull_request.number }}/head:pr-head git fetch --quiet origin main BASE=$(git merge-base pr-head origin/main) - HEAD=pr-head + HEAD=$(git rev-parse pr-head) else echo "Push to riscv" # 统一用 riscv 作为 baseline From 9fb76e6fd981bf5938600c15c6311e6a982b9a48 Mon Sep 17 00:00:00 2001 From: Yixuan Chen Date: Sun, 26 Apr 2026 00:49:38 +0800 Subject: [PATCH 10/16] Fix bytes_to_scalar for float/complex on RISC-V bytes_to_scalar previously round-tripped raw bytes through Python float/complex values (via ctypes) before constructing the tensor. This loses NaN bit patterns on architectures (such as RISC-V) that canonicalize NaNs in floating-point loads/conversions, causing test_bytes_to_scalar_cpu_{float32,float64,complex64,complex128} to fail with mismatched storage bytes. Construct the scalar tensor by writing the raw bytes directly into its untyped storage so all input bit patterns (including NaN payloads) are preserved exactly. --- torch/testing/_internal/common_utils.py | 37 +++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/torch/testing/_internal/common_utils.py b/torch/testing/_internal/common_utils.py index 608c8ee4b84b3..e50f2f00c9bd4 100644 --- a/torch/testing/_internal/common_utils.py +++ b/torch/testing/_internal/common_utils.py @@ -5668,27 +5668,22 @@ def check_bytes(byte_list): if not (0 <= byte <= 255): raise AssertionError(f"byte value out of range: expected 0 <= byte <= 255, got {byte}") - if dtype.is_complex: - if len(byte_list) != (num_bytes * 2): - raise AssertionError( - f"expected len(byte_list) == {num_bytes * 2} for complex dtype, got {len(byte_list)}" - ) - check_bytes(byte_list) - real = ctype.from_buffer((ctypes.c_byte * num_bytes)( - *byte_list[:num_bytes])).value - imag = ctype.from_buffer((ctypes.c_byte * num_bytes)( - *byte_list[num_bytes:])).value - res = real + 1j * imag - else: - if len(byte_list) != num_bytes: - raise AssertionError( - f"expected len(byte_list) == {num_bytes}, got {len(byte_list)}" - ) - check_bytes(byte_list) - res = ctype.from_buffer((ctypes.c_byte * num_bytes)( - *byte_list)).value - - return torch.tensor(res, device=device, dtype=dtype) + expected_len = num_bytes * 2 if dtype.is_complex else num_bytes + if len(byte_list) != expected_len: + raise AssertionError( + f"expected len(byte_list) == {expected_len}" + f"{' for complex dtype' if dtype.is_complex else ''}, got {len(byte_list)}" + ) + check_bytes(byte_list) + + # Write bytes directly into storage to preserve exact bit patterns + # (e.g. NaN payloads, which are not preserved when round-tripping through + # Python float/complex, especially on architectures like RISC-V that + # canonicalize NaNs). + res = torch.empty((), dtype=dtype, device=device) + src = torch.tensor(byte_list, dtype=torch.uint8, device=device) + res.untyped_storage().copy_(src.untyped_storage()) + return res def copy_func(f): From 08d645aaefd14905891180c0b920a938bc593127 Mon Sep 17 00:00:00 2001 From: Yixuan Chen Date: Wed, 22 Apr 2026 15:27:45 +0800 Subject: [PATCH 11/16] Fix test_float_to_int_conversion_nonfinite for RISC-V RISC-V converts non-finite floats to integers by saturating: -inf -> min, inf/nan -> max for wider int types. Add IS_RISCV64 flag and RISC-V-specific reference values. Co-Authored-By: Claude Opus 4.7 --- test/test_tensor_creation_ops.py | 8 ++++++++ torch/testing/_internal/common_utils.py | 1 + 2 files changed, 9 insertions(+) diff --git a/test/test_tensor_creation_ops.py b/test/test_tensor_creation_ops.py index b9e8e537b53cb..99396cea12f5b 100644 --- a/test/test_tensor_creation_ops.py +++ b/test/test_tensor_creation_ops.py @@ -32,6 +32,7 @@ IS_SANDCASTLE, IS_S390X, IS_ARM64, + IS_RISCV64, parametrize, TEST_WITH_TORCHDYNAMO, xfailIfTorchDynamo, @@ -1109,6 +1110,13 @@ def test_float_to_int_conversion_nonfinite(self, device, dtype): if dtype == torch.bool: refs = (True, True, True) + elif IS_RISCV64: + if dtype in (torch.int32, torch.int64): + refs = (torch.iinfo(dtype).min, torch.iinfo(dtype).max, torch.iinfo(dtype).max) + elif dtype == torch.uint8: + refs = (0, torch.iinfo(dtype).max, torch.iinfo(dtype).max) + elif dtype in (torch.int8, torch.int16): + refs = (0, -1, -1) elif IS_ARM64: refs = (torch.iinfo(dtype).min, torch.iinfo(dtype).max, 0) if dtype in (torch.int8, torch.int16): diff --git a/torch/testing/_internal/common_utils.py b/torch/testing/_internal/common_utils.py index e50f2f00c9bd4..25e0c69804234 100644 --- a/torch/testing/_internal/common_utils.py +++ b/torch/testing/_internal/common_utils.py @@ -1438,6 +1438,7 @@ def printErrors(self) -> None: IS_PPC = platform.machine() == "ppc64le" IS_X86 = platform.machine() in ('x86_64', 'i386') IS_ARM64 = platform.machine() in ('arm64', 'aarch64', 'ARM64') +IS_RISCV64 = platform.machine() == 'riscv64' IS_S390X = platform.machine() == "s390x" IS_AVX512_VNNI_SUPPORTED = torch.cpu.get_capabilities().get("avx512_vnni", False) IS_CPU_EXT_SVE_SUPPORTED = torch.cpu.get_capabilities().get("sve", False) From 1e6be1a1af4cb7c0cde3337e2b8005adbf67c9fc Mon Sep 17 00:00:00 2001 From: Yixuan Chen Date: Sat, 9 May 2026 15:59:22 +0800 Subject: [PATCH 12/16] Skip test if no qengine --- test/test_torch.py | 11 +++++++++-- torch/testing/_internal/common_utils.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/test_torch.py b/test/test_torch.py index cf95adbc2c916..f93661cf386f4 100644 --- a/test/test_torch.py +++ b/test/test_torch.py @@ -43,7 +43,7 @@ wrapDeterministicFlagAPITest, DeterministicGuard, CudaSyncGuard, bytes_to_scalar, parametrize, noncontiguous_like, AlwaysWarnTypedStorageRemoval, TEST_WITH_TORCHDYNAMO, xfailIfTorchDynamo, - xfailIfS390X, set_warn_always_context, decorateIf, isRocmArchAnyOf) + xfailIfS390X, xfailIfRISCV, set_warn_always_context, decorateIf, isRocmArchAnyOf) from multiprocessing.reduction import ForkingPickler from torch.testing._internal.common_device_type import ( expectedFailureMeta, @@ -9594,14 +9594,21 @@ def test_type(self): # FIXME: port to a quantization test suite @xfailIfS390X + @xfailIfRISCV def test_qengine(self): qengines = torch.backends.quantized.supported_engines + if not qengines: + self.skipTest("No quantized engines supported on this platform") original_qe = torch.backends.quantized.engine for qe in qengines: torch.backends.quantized.engine = qe if torch.backends.quantized.engine != qe: raise AssertionError(f"qengine not set successfully: expected {qe}, got {torch.backends.quantized.engine}") - torch.backends.quantized.engine = original_qe + # On platforms where no qengine is compiled in as the default (e.g. RISC-V), + # the initial engine reads as "none" (NoQEngine), which is not a valid value + # to pass back to _set_qengine. Only restore if it was a real engine. + if original_qe != "none": + torch.backends.quantized.engine = original_qe def test_terminate_handler_on_crash(self): cmd = [sys.executable, '-c', "import os; os.environ[\"TORCH_CUSTOM_TERMINATE\"] ='1'; \ diff --git a/torch/testing/_internal/common_utils.py b/torch/testing/_internal/common_utils.py index 25e0c69804234..099382a195ed0 100644 --- a/torch/testing/_internal/common_utils.py +++ b/torch/testing/_internal/common_utils.py @@ -1444,6 +1444,7 @@ def printErrors(self) -> None: IS_CPU_EXT_SVE_SUPPORTED = torch.cpu.get_capabilities().get("sve", False) IS_CPU_CAPABILITY_SVE = torch._C._get_cpu_capability() in ("SVE128", "SVE256") IS_CPU_CAPABILITY_SVE256 = torch._C._get_cpu_capability() == "SVE256" +IS_RISCV = platform.machine() in ('riscv64', 'riscv32') if IS_WINDOWS: @contextmanager @@ -2266,6 +2267,9 @@ def wrap_fn(self, *args, **kwargs): def xfailIfS390X(func): return unittest.expectedFailure(func) if IS_S390X else func +def xfailIfRISCV(func): + return unittest.expectedFailure(func) if IS_RISCV else func + def xfailIf(condition): def wrapper(func): if condition: From a9a4cbdf36ef4f52d123acd551c2f498312f4323 Mon Sep 17 00:00:00 2001 From: XYenChi Date: Wed, 20 May 2026 14:15:08 +0800 Subject: [PATCH 13/16] Replace offical cpuinfo repo (#18) --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 076ce38ac7938..35feb14cec9ae 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,7 +41,7 @@ [submodule "third_party/cpuinfo"] ignore = dirty path = third_party/cpuinfo - url = https://github.com/pytorch/cpuinfo.git + url = https://github.com/RuyiAI-Stack/cpuinfo.git [submodule "third_party/python-peachpy"] ignore = dirty path = third_party/python-peachpy From d3d51ee2704a8b8d940c54fe7261fd1cbef3f39d Mon Sep 17 00:00:00 2001 From: vimer Date: Sun, 24 May 2026 23:34:18 +0800 Subject: [PATCH 14/16] [ci] split core and full ci (#20) --- .github/workflows/ci-riscv64.yml | 79 +++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index 44cd575a01e01..127e0a01e36eb 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -1,5 +1,8 @@ # Note: this runner is provided externally, so we minimize its access to # secrets. + +name: CI (riscv64) + on: push: branches: [riscv] @@ -7,8 +10,6 @@ on: pull_request_target: types: [opened, synchronize, reopened] -name: CI (riscv64) - permissions: contents: read # No permissions to secrets. @@ -23,9 +24,17 @@ env: CARGO_TERM_COLOR: always jobs: - build: - name: Build and test + core-ci: + name: pytorch-riscv64-core-ci runs-on: [self-hosted, linux, amd64] + + outputs: + base_commit: ${{ steps.meta.outputs.base_commit }} + head_commit: ${{ steps.meta.outputs.head_commit }} + patch_file: ${{ steps.patch.outputs.patch_file }} + ci_result_base_url: ${{ steps.jenkins.outputs.ci_result_base_url }} + ci_stat_url: ${{ steps.jenkins.outputs.ci_stat_url }} + # This is in its own separate environment. environment: riscv64 steps: @@ -38,7 +47,7 @@ jobs: - name: Extract PR info run: | echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV - echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> GITHUB_ENV + echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV - name: Diff base and head run: | @@ -50,7 +59,7 @@ jobs: echo "Base ref: $BASE_REF" echo "Head ref: $HEAD_REF" - # 强约束: PR 必须基于 riscv + # must based on riscv if [ "$BASE_REF" != "riscv" ]; then echo "ERROR: PR must target 'riscv' branch, got '$BASE_REF'" exit 1 @@ -97,4 +106,60 @@ jobs: - name: Trigger Jenkins Job run: | - bash /home/jenkins/scripts/jenkins-run.sh $BASE_COMMIT $PATCH_FILE + set -euo pipefail + + BASE=${{ steps.meta.outputs.base_commit }} + PATCH=${{ steps.patch.outputs.patch_file }} + + bash /home/jenkins/scripts/jenkins-run.sh $BASE_COMMIT $PATCH_FILE | tee jenkins.log + + CI_STAT_URL=$(grep -oE 'https://[^ ]+/pytorch-ci-stat\.json' jenkins.log | tail -n1) + + if [[ -z "$CI_STAT_URL" ]]; then + echo "ERROR: cannot find pytorch-ci-stat.json URL from Jenkins log" + exit 1 + fi + + CI_RESULT_BASE_URL="${CI_STAT_URL%/pytorch-ci-stat.json}" + + echo "ci_stat_url=$CI_STAT_URL" >> "$GITHUB_OUTPUT" + echo "ci_result_base_url=$CI_RESULT_BASE_URL" >> "$GITHUB_OUTPUT" + + echo "CI_STAT_URL=$CI_STAT_URL" + echo "CI_RESULT_BASE_URL=$CI_RESULT_BASE_URL" + +full-ci: + name: pytorch-riscv64-full-ci + runs-on: [self-hosted, linux, amd64] + needs: core-ci + if: always() + continue-on-error: true + + steps: + - name: Query existing full test result + shell: bash + run: | + set -euo pipefail + + BASE_URL="${{ needs.core-ci.outputs.ci_result_base_url }}" + STAT_URL="${BASE_URL}/pytorch-ci-stat.json" + + echo "STAT_URL=$STAT_URL" + + curl -fsSL "$STAT_URL" -o pytorch-ci-stat.json + + echo "==== FULL TEST STAT ====" + cat pytorch-ci-stat.json + echo + + FAILED=$(jq '.failed | length' pytorch-ci-stat.json) + + if [[ "$FAILED" != "0" ]]; then + echo "==== FULL TEST FAILED ====" + echo "failed cases: $FAILED" + exit 1 + fi + + echo "==== FULL TEST PASSED ====" + echo "full test no failures" + From 3a5cb48e42ee82437c871b0b0b5f118a8daeb8b5 Mon Sep 17 00:00:00 2001 From: vimer Date: Sun, 24 May 2026 23:51:12 +0800 Subject: [PATCH 15/16] [ci] fix ci workflow and other issue (#21) for action, these yaml must be merged first then take effect, so merge it skipping ci --- .github/workflows/ci-riscv64.yml | 63 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index 127e0a01e36eb..8c53debb93a8e 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -50,6 +50,7 @@ jobs: echo "HEAD_SHA=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV - name: Diff base and head + id: meta run: | if [[ "${{ github.event_name }}" = "pull_request" || "${{ github.event_name }}" == "pull_request_target" ]]; then echo "Push PR build" @@ -84,10 +85,14 @@ jobs: echo "BASE_COMMIT=$BASE" >> $GITHUB_ENV echo "HEAD_COMMIT=$HEAD" >> $GITHUB_ENV + echo "base_commit=$BASE" >> "$GITHUB_OUTPUT" + echo "head_commit=$HEAD" >> "$GITHUB_OUTPUT" + echo "Base: $BASE" echo "Head: $HEAD" - name: Generate patch + id: patch run: | echo "Generating patch..." @@ -102,16 +107,18 @@ jobs: cp $PATCH_NAME /home/jenkins/patch/ cat /home/jenkins/patch/$PATCH_NAME - echo "PATCH_FILE=$PATCH_NAME" >> $GITHUB_ENV + echo "PATCH_FILE=$PATCH_NAME" >> "$GITHUB_ENV" + echo "patch_file=$PATCH_NAME" >> "$GITHUB_OUTPUT" - name: Trigger Jenkins Job + id: jenkins run: | set -euo pipefail BASE=${{ steps.meta.outputs.base_commit }} PATCH=${{ steps.patch.outputs.patch_file }} - bash /home/jenkins/scripts/jenkins-run.sh $BASE_COMMIT $PATCH_FILE | tee jenkins.log + bash /home/jenkins/scripts/jenkins-run.sh $BASE $PATCH | tee jenkins.log CI_STAT_URL=$(grep -oE 'https://[^ ]+/pytorch-ci-stat\.json' jenkins.log | tail -n1) @@ -128,38 +135,38 @@ jobs: echo "CI_STAT_URL=$CI_STAT_URL" echo "CI_RESULT_BASE_URL=$CI_RESULT_BASE_URL" -full-ci: - name: pytorch-riscv64-full-ci - runs-on: [self-hosted, linux, amd64] - needs: core-ci - if: always() - continue-on-error: true + full-ci: + name: pytorch-riscv64-full-ci + runs-on: [self-hosted, linux, amd64] + needs: core-ci + if: always() + continue-on-error: true - steps: - - name: Query existing full test result - shell: bash - run: | - set -euo pipefail + steps: + - name: Query existing full test result + shell: bash + run: | + set -euo pipefail - BASE_URL="${{ needs.core-ci.outputs.ci_result_base_url }}" - STAT_URL="${BASE_URL}/pytorch-ci-stat.json" + BASE_URL="${{ needs.core-ci.outputs.ci_result_base_url }}" + STAT_URL="${BASE_URL}/pytorch-ci-stat.json" - echo "STAT_URL=$STAT_URL" + echo "STAT_URL=$STAT_URL" - curl -fsSL "$STAT_URL" -o pytorch-ci-stat.json + curl -fsSL "$STAT_URL" -o pytorch-ci-stat.json - echo "==== FULL TEST STAT ====" - cat pytorch-ci-stat.json - echo + echo "==== FULL TEST STAT ====" + cat pytorch-ci-stat.json + echo - FAILED=$(jq '.failed | length' pytorch-ci-stat.json) + FAILED=$(jq '.failed | length' pytorch-ci-stat.json) - if [[ "$FAILED" != "0" ]]; then - echo "==== FULL TEST FAILED ====" - echo "failed cases: $FAILED" - exit 1 - fi + if [[ "$FAILED" != "0" ]]; then + echo "==== FULL TEST FAILED ====" + echo "failed cases: $FAILED" + exit 1 + fi - echo "==== FULL TEST PASSED ====" - echo "full test no failures" + echo "==== FULL TEST PASSED ====" + echo "full test no failures" From 96cf21946af9dc3b4739aaa1b83bac4e91384c76 Mon Sep 17 00:00:00 2001 From: Bo YU Date: Thu, 21 May 2026 04:04:17 +0000 Subject: [PATCH 16/16] [ci] clear unused flags But the most important here is, to only run core test cases --- .github/workflows/ci-riscv64.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci-riscv64.yml b/.github/workflows/ci-riscv64.yml index 8c53debb93a8e..273034f514d79 100644 --- a/.github/workflows/ci-riscv64.yml +++ b/.github/workflows/ci-riscv64.yml @@ -18,11 +18,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true -# FIXME: Drop this -env: - RUSTFLAGS: -D warnings - CARGO_TERM_COLOR: always - jobs: core-ci: name: pytorch-riscv64-core-ci