Skip to content
Merged
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
70 changes: 55 additions & 15 deletions .claude/skills/github-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,59 @@ description: Create a GitHub pull request after committing, rebasing, and pushin

# PyPTO GitHub Pull Request Workflow

## Prerequisites
## Workflow Steps

⚠️ **MANDATORY**: Use the `/git-commit` skill to commit all changes. This runs the full code review, testing, and linting workflow. Do not commit directly.
1. **Prepare branch and commit** (if on main or uncommitted changes)
2. Check for existing PR (exit if found)
3. Fetch upstream changes
4. Rebase onto upstream/main
5. Resolve conflicts if needed
6. Push to fork with `--force-with-lease`
7. Create PR using gh CLI

## Workflow Steps
## Step 1: Prepare Branch and Commit

**Check current state:**

```bash
BRANCH_NAME=$(git branch --show-current)
git status --porcelain # Check for uncommitted changes
git fetch upstream 2>/dev/null || git fetch origin # Fetch latest
if git rev-parse --verify upstream/main >/dev/null 2>&1; then
BASE_REF=upstream/main
else
BASE_REF=origin/main
fi
git rev-list HEAD --not "$BASE_REF" --count # Commits ahead of base
```

A branch "needs a new branch" when it is effectively on main — either the branch name is `main`/`master`, **or** it has zero commits ahead of upstream/main (e.g., a local branch that was never diverged).

**Decision logic:**

| Needs new branch? | Uncommitted changes? | Action |
| ----------------- | -------------------- | ------ |
| Yes | Yes | Create new branch, then commit via `/git-commit` |
| Yes | No | Error — nothing to PR. Tell user to make changes first |
| No | Yes | Commit on current branch via `/git-commit` |
| No | No | Skip — already committed on a feature branch |

**If a new branch is needed:**

1. Ask the user for a branch name (suggest one based on the changes)
2. Create and switch to the new branch:

```bash
git checkout -b <branch-name>
```

1. Commit via `/git-commit` skill (mandatory — runs code review, testing, linting)

**If on an existing feature branch with uncommitted changes:**

1. Check for existing PR (exit if found)
2. Fetch upstream changes
3. Rebase onto upstream/main
4. Resolve conflicts if needed
5. Push to fork with `--force-with-lease`
6. Create PR using gh CLI
Commit via `/git-commit` skill before proceeding.

## Step 1: Check for Existing PR
## Step 2: Check for Existing PR

```bash
BRANCH_NAME=$(git branch --show-current)
Expand All @@ -27,14 +66,14 @@ gh pr list --head "$BRANCH_NAME" --state open

**If PR exists**: Display with `gh pr view` and exit immediately.

## Step 2: Fetch Upstream
## Step 3: Fetch Upstream

```bash
git remote add upstream https://github.com/hw-native-sys/pypto.git # If needed
git fetch upstream
```

## Step 3: Rebase
## Step 4: Rebase

```bash
git rebase upstream/main # Or user-specified branch
Expand All @@ -50,7 +89,7 @@ git rebase --continue
# If stuck: git rebase --abort
```

## Step 4: Push
## Step 5: Push

```bash
# First push
Expand All @@ -62,7 +101,7 @@ git push --force-with-lease origin BRANCH_NAME

⚠️ **Use `--force-with-lease`** - safer than `--force`, fails if remote has unexpected changes.

## Step 5: Create PR
## Step 6: Create PR

**Check gh CLI**:

Expand Down Expand Up @@ -112,8 +151,9 @@ EOF

## Checklist

- [ ] Branch prepared (created from main if needed)
- [ ] Changes committed via `/git-commit` (if uncommitted changes existed)
- [ ] No existing PR for branch (exit if found)
- [ ] Changes committed via git-commit
- [ ] Fetched upstream and rebased successfully
- [ ] Conflicts resolved
- [ ] Pushed with `--force-with-lease`
Expand Down
5 changes: 5 additions & 0 deletions tests/st/codegen/test_add_mul_orch_cce_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import os

import pypto.language as pl
import pytest
from pypto import DataType, ir
from pypto.backend import BackendType

Expand Down Expand Up @@ -185,3 +186,7 @@ def test_add_mul_orch_cce_codegen(tmp_path):
passes_dump_dir = os.path.join(output_dir, "passes_dump")
assert os.path.exists(passes_dump_dir), "Passes dump directory should exist"
assert os.path.isdir(passes_dump_dir), "Passes dump path should be a directory"


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/st/harness/core/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from datetime import datetime
from pathlib import Path

import pytest
from pypto.backend import BackendType, set_backend_type

from harness.adapters.golden_generator import GoldenGenerator
Expand Down Expand Up @@ -273,3 +274,7 @@ def summary(self, results: dict[str, TestResult]) -> str:
lines.append(f" - {name}: {result.error}")

return "\n".join(lines)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/st/runtime/test_dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import Any

import pypto.language as pl
import pytest
from harness.core.harness import DataType, PTOTestCase, TensorSpec


Expand Down Expand Up @@ -132,3 +133,7 @@ def test_vector_dag_128x128(self, test_runner):
test_case = TestVectorDAG()
result = test_runner.run(test_case)
assert result.passed, f"Test failed for vector DAG: {result.error}"


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/st/runtime/test_elementwise.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,7 @@ def test_tile_add_ptoas_strategy(self, test_runner):
test_case = TestTileAddWithPTOAS()
result = test_runner.run(test_case)
assert result.passed, f"Test failed: {result.error}"


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/st/runtime/test_matmul.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import Any

import pypto.language as pl
import pytest
import torch
from harness.core.harness import DataType, PTOTestCase, TensorSpec

Expand Down Expand Up @@ -74,3 +75,7 @@ def test_matmul_64x64(self, test_runner):
test_case = TestMatmul()
result = test_runner.run(test_case)
assert result.passed, f"Test failed: {result.error}"


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/backend/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

"""Tests for Backend, SoC construction, and serialization."""

import pytest
from pypto import ir
from pypto.backend import (
Cluster,
Expand Down Expand Up @@ -115,3 +116,7 @@ def test_soc_convenience_constructor(self):
assert soc.total_die_count() == 2
assert soc.total_cluster_count() == 6
assert soc.total_core_count() == 12


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/backend/test_backend_910b.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import tempfile
from pathlib import Path

import pytest
from pypto import ir
from pypto.backend import Backend910B_CCE, Backend910B_PTO

Expand Down Expand Up @@ -162,3 +163,7 @@ def test_export_backend(self):
assert Path(temp_path).exists()
finally:
Path(temp_path).unlink()


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/codegen/test_cce_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""Unit tests for CCECodegen class."""

import pypto.language as pl
import pytest
from pypto import DataType, backend, codegen, ir
from pypto.backend import BackendType
from pypto.ir.builder import IRBuilder
Expand Down Expand Up @@ -365,3 +366,7 @@ def test_matmul_acc(
# Verify both TMATMUL and TMATMUL_ACC are generated
assert "TMATMUL(" in code
assert "TMATMUL_ACC(" in code


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/codegen/test_orchestration_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import textwrap

import pypto.language as pl
import pytest
from pypto import backend, codegen
from pypto.backend import BackendType

Expand Down Expand Up @@ -887,3 +888,7 @@ def orch_dim(

# tensor.dim generates int64_t assignment
assert "int64_t d0 = 64" in code


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/codegen/test_pto_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"""

import pypto.language as pl
import pytest
from pypto import backend, codegen
from pypto.backend import BackendType
from pypto.ir import OptimizationStrategy, PassManager
Expand Down Expand Up @@ -367,3 +368,7 @@ def test_func(self, a: pl.Tensor[[32, 32], pl.FP32], b: pl.Tensor[[32, 32], pl.F
assert "func.func @test_func" in code1
assert "func.func @test_func" in code2
assert code1 == code2 # Should produce identical output


if __name__ == "__main__":
pytest.main([__file__, "-v"])
9 changes: 4 additions & 5 deletions tests/ut/codegen/test_pto_codegen_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@
and verifies the generated orchestration code.
"""

import unittest

import pypto.language as pl
import pytest
from pypto import DataType, backend, codegen, ir
from pypto.backend import BackendType
from pypto.ir.pass_manager import OptimizationStrategy, PassManager
Expand Down Expand Up @@ -481,8 +480,8 @@ def build_block_ops_test_program(dtype: DataType = DataType.FP32):
return BlockOperationsTest


class Test910BBlockOpsCodegen(unittest.TestCase):
"""Unit tests for 910B PTO backend block-level operations code generation."""
class Test910BBlockOpsCodegen:
"""Tests for 910B PTO backend block-level operations code generation."""

def test_block_ops_codegen(self):
"""Test code generation for all block-level operations."""
Expand Down Expand Up @@ -532,4 +531,4 @@ def test_block_ops_codegen(self):


if __name__ == "__main__":
unittest.main()
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/codegen/test_type_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,7 @@ def test_convert_event_id_invalid(self, invalid_id):
converter = codegen.TypeConverter()
with pytest.raises(ValueError, match=rf"Event ID must be in range \[0, 7\].*got {invalid_id}"):
converter.ConvertEventId(invalid_id)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/ir/high_level/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,7 @@ def test_function_different_body_types_not_equal(self):
func2 = ir.Function("test_func", [x], [ir.ScalarType(dtype)], body2, span)

assert not ir.structural_equal(func1, func2)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/ir/high_level/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,7 @@ def test_program_different_from_base_irnode_not_equal(self):

# Compare with a Var (different IRNode type)
assert not ir.structural_equal(program, x)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/ir/operators/test_operator_spans.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,7 @@ def test_all_reverse_operators(self):
f"Reverse operator {op_name} failed"
)
assert result.span.begin_line > 0, f"Reverse operator {op_name} has invalid line number"


if __name__ == "__main__":
pytest.main([__file__, "-v"])
6 changes: 5 additions & 1 deletion tests/ut/ir/statements/test_eval_stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------

import pytest
from pypto import ir


Expand Down Expand Up @@ -79,3 +79,7 @@ def test_sync_ops_enum_usage():
ir.create_op_call(
"system.sync_src", [], {"set_pipe": ir.PipeType.MTE2, "wait_pipe": ir.PipeType.V, "event_id": 0}, span
)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/ir/statements/test_scope_stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""Unit tests for ScopeStmt class."""

import pypto.language as pl
import pytest
from pypto import DataType, ir


Expand Down Expand Up @@ -60,3 +61,7 @@ def main(self, x: pl.Tensor[[64], pl.FP32]) -> pl.Tensor[[64], pl.FP32]:
# Print and verify it contains "with pl.incore():"
printed = ir.python_print(TestProgram)
assert "with pl.incore():" in printed


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/ir/statements/test_seq_stmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,7 @@ def test_seq_stmts_structural_equal_multiple_statements(self):
seq_stmts2 = ir.SeqStmts([assign2_1, assign2_2, assign2_3], span)

ir.assert_structural_equal(seq_stmts1, seq_stmts2, enable_auto_mapping=True)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
4 changes: 4 additions & 0 deletions tests/ut/ir/statements/test_while_stmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,7 @@ def test_while_stmt_structural_hash(self):
condition2 = ir.Lt(x, ir.ConstInt(20, dtype, span), dtype, span)
while_stmt3 = ir.WhileStmt(condition2, [], assign, [], span)
assert ir.structural_hash(while_stmt1) != ir.structural_hash(while_stmt3)


if __name__ == "__main__":
pytest.main([__file__, "-v"])
5 changes: 5 additions & 0 deletions tests/ut/ir/transforms/test_basic_memory_reuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""Tests for BasicMemoryReusePass using @pl.program with pl.Tile type."""

import pypto.language as pl
import pytest
from pypto import ir, passes
from pypto.ir.pass_manager import OptimizationStrategy, PassManager

Expand Down Expand Up @@ -430,3 +431,7 @@ def main(
_assert_shares_memref(func, "tile_a", "_tile_b")
# tile_d should reuse the shared buffer (either tile_a or tile_b, they're the same)
_assert_shares_memref(func, "tile_d", "tile_a")


if __name__ == "__main__":
pytest.main([__file__, "-v"])
Loading