Repository navigation
docs(kernels): add kernel framework docs and the LLVM interop design #402
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, wjy_dev, jzj_dev] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| # 课题功能测试:所有14个模块的单元测试 + 集成测试 | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| test: | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'self-hosted' }} | |
| timeout-minutes: 20 | |
| env: | |
| # Pin the scheduler's timing authority so both runners agree; see the | |
| # toolchain step below. | |
| LLVM_MCA: llvm-mca-18 | |
| steps: | |
| - name: Checkout pull request on isolated runner | |
| if: github.event_name == 'pull_request' | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 | |
| with: | |
| persist-credentials: false | |
| # ONNX before/after integration tests read the pinned PR #89 commit. | |
| fetch-depth: 0 | |
| - name: Set up Python for pull request | |
| if: github.event_name == 'pull_request' | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Sync from local mirror (skip unstable GitHub checkout) | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| echo "=== CI Sync ===" | |
| retry() { | |
| local desc="$1" rc=0; shift | |
| for i in 1 2 3; do | |
| if "$@" --depth=1; then rc=0; break; fi | |
| rc=$? | |
| if [ "$i" -lt 3 ]; then | |
| echo "::warning::$desc failed, retry #$i in $((i*3))s" | |
| sleep $((i * 3)) | |
| else | |
| echo "::error::$desc failed after 3 attempts" | |
| fi | |
| done | |
| return $rc | |
| } | |
| MIRROR=/opt/ScratchV | |
| WORKSPACE=/opt/actions-runner/_work/ScratchV/ScratchV | |
| rm -rf "$WORKSPACE" | |
| cp -a "$MIRROR" "$WORKSPACE" | |
| cd "$WORKSPACE" | |
| echo "Fetching origin main..." | |
| retry "git fetch origin main" git fetch origin main || exit 1 | |
| echo "Checking out $GITHUB_SHA..." | |
| if ! git -c advice.detachedHead=false checkout -f "$GITHUB_SHA"; then | |
| echo "::warning::Checkout failed, fetching SHA directly from origin..." | |
| git fetch origin "$GITHUB_SHA" || true | |
| if git -c advice.detachedHead=false checkout -f "$GITHUB_SHA"; then | |
| echo "::notice::Checkout successful after direct fetch: $(git log -1 --format='%h %ai %s')" | |
| else | |
| echo "::error::GITHUB_SHA ($GITHUB_SHA) not found — checkout failed" | |
| exit 1 | |
| fi | |
| else | |
| echo "::notice::Checkout successful: $(git log -1 --format='%h %ai %s')" | |
| fi | |
| - name: Install dependencies | |
| run: | | |
| set -euo pipefail | |
| if python -c 'import sys; sys.exit(0 if sys.version_info[:2] == (3, 12) else 1)'; then | |
| PY=python | |
| elif command -v python3.12 >/dev/null 2>&1; then | |
| PY=python3.12 | |
| else | |
| echo "Python 3.12 not found" >&2 | |
| exit 1 | |
| fi | |
| echo "PY=$PY" >> "$GITHUB_ENV" | |
| echo "Using python: $($PY -c 'import sys; print(sys.executable, sys.version)')" | |
| $PY -m pip install --upgrade pip setuptools wheel | |
| $PY -m pip install -e ".[all]" | |
| $PY -m pip install "pytest>=7,<10" | |
| - name: Run Topic 04 PassManager regressions | |
| id: topic04_pass_manager | |
| env: | |
| PYTHONHASHSEED: "0" | |
| run: | | |
| set -euo pipefail | |
| mkdir -p benchmark_reports | |
| $PY -m pytest \ | |
| tests/test_pass_manager.py \ | |
| tests/test_pass_registry.py \ | |
| tests/test_optimizer.py \ | |
| tests/test_optimizer_advanced.py \ | |
| -v --tb=short \ | |
| --junit-xml=benchmark_reports/topic04_pass_manager.xml \ | |
| | tee benchmark_reports/topic04_pass_manager.txt | |
| - name: Summarize Topic 04 PassManager results | |
| if: always() | |
| env: | |
| TOPIC04_OUTCOME: ${{ steps.topic04_pass_manager.outcome }} | |
| run: | | |
| { | |
| echo "## Topic 04 PassManager" | |
| echo "Result: $TOPIC04_OUTCOME" | |
| echo "" | |
| echo "Coverage: pass registration, enable/disable switches, constant folding, functional pipelines, and optimizer regressions." | |
| echo "" | |
| echo "JUnit XML and detailed test output are included in the test-reports artifact when tests run." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Run IR interpreter and LICM regressions | |
| timeout-minutes: 5 | |
| env: | |
| PYTHONHASHSEED: "0" | |
| OMP_NUM_THREADS: "1" | |
| OPENBLAS_NUM_THREADS: "1" | |
| MKL_NUM_THREADS: "1" | |
| run: | | |
| mkdir -p benchmark_reports | |
| $PY -m pytest tests/test_ir_interpreter*.py tests/test_licm_safety.py -q \ | |
| --junit-xml=benchmark_reports/ir_interpreter_tests.xml | |
| - name: Prepare RISC-V test tools | |
| run: | | |
| set -euo pipefail | |
| # llvm-mca is the scheduler's timing authority. The checked-in benchmark | |
| # numbers and the scheduler test expectations are calibrated to LLVM 18. | |
| # Ubuntu's `llvm` metapackage resolves to 14 on 22.04 and 18 on 24.04, so | |
| # pin the major version instead of taking whatever `llvm` happens to be. | |
| if ! command -v clang >/dev/null || ! command -v ld.lld >/dev/null \ | |
| || ! command -v qemu-riscv32 >/dev/null || ! command -v llvm-mca-18 >/dev/null; then | |
| # The self-hosted runner has no passwordless sudo; there the toolchain is | |
| # provisioned out of band and the checks below are the assertion. | |
| if sudo -n true 2>/dev/null; then | |
| sudo apt-get update | |
| sudo apt-get install --no-install-recommends -y clang lld qemu-user llvm-18 \ | |
| || echo "::warning::llvm-18 is not available from this runner's repositories" | |
| else | |
| echo "::warning::no passwordless sudo; using the runner's preinstalled toolchain" | |
| fi | |
| fi | |
| missing="" | |
| for tool in clang ld.lld qemu-riscv32 llvm-mca-18; do | |
| command -v "$tool" >/dev/null 2>&1 || missing="$missing $tool" | |
| done | |
| if [ -n "$missing" ]; then | |
| echo "::error::missing tools:$missing -- provision them on this runner as root; CI cannot install them (no passwordless sudo)" | |
| echo "::notice::Ubuntu 22.04's default \`llvm\` package is 14, so \`llvm-mca-18\` needs the apt.llvm.org archive; the scheduler's expectations are calibrated to LLVM 18 (docs/topics/18-指令调度器/README.md)." | |
| exit 1 | |
| fi | |
| $PY -c "from scratchv.backend.llvm_mca import check_version; v = check_version('llvm-mca-18'); print(v); assert 'LLVM version 18.' in v, 'scheduler timing requires LLVM 18, got: ' + v" | |
| # Includes scheduling and standalone CNN execution regressions. | |
| - name: Run all topic tests | |
| env: | |
| PYTHONHASHSEED: "0" | |
| SCRATCHV_REQUIRE_RISCV_EXECUTION: "1" | |
| run: | | |
| mkdir -p benchmark_reports | |
| $PY -m pytest tests/ -v --tb=short \ | |
| --junit-xml=benchmark_reports/test_results.xml \ | |
| --ignore=tests/test_simulator.py | |
| - name: Topic 01 DSL frontend regressions | |
| run: | | |
| $PY -m pytest \ | |
| tests/test_dsl_control_flow.py \ | |
| tests/test_dsl_control_flow_benchmark.py \ | |
| -v --tb=short | |
| - name: Run assembly-beautifier regressions | |
| run: | | |
| $PY -m pytest \ | |
| tests/test_parser_for_beautifier.py \ | |
| tests/test_asm_beautifier_comments.py \ | |
| tests/test_asm_beautifier_formatting.py \ | |
| tests/test_asm_beautifier_integration.py \ | |
| tests/test_asm_beautifier_blackbox.py \ | |
| -v --tb=short | |
| - name: Run constant-merge regressions | |
| run: | | |
| $PY -m pytest \ | |
| tests/test_const_merge.py \ | |
| tests/test_backend.py::TestConstMergeIntegration \ | |
| tests/test_simulator.py::TestStubProfiledMachine \ | |
| -v --tb=short | |
| - name: Run PR #37 register-allocation regressions | |
| run: | | |
| $PY -m pytest tests/test_pr37_regression.py -v --tb=short | |
| - name: Run topic13 peephole regressions | |
| run: | | |
| $PY -m pytest tests/test_asm_peephole*.py -v --tb=short | |
| - name: Run topic13 peephole CLI smoke | |
| run: | | |
| $PY -m scratchv.backend.asm_peephole --list-rules | tee /tmp/peephole_rules.txt | |
| grep -q "addi+addi fusion" /tmp/peephole_rules.txt | |
| if grep -q "redundant mv pair elimination" /tmp/peephole_rules.txt; then | |
| echo "unsound rule must not be listed" >&2 | |
| exit 1 | |
| fi | |
| $PY -m scratchv.backend.asm_peephole \ | |
| tests/fixtures/asm_peephole/input_addi_fusion.s \ | |
| -o /tmp/peephole_out.s --report --json | tee /tmp/peephole_report.json | |
| $PY -c "import json; d=json.load(open('/tmp/peephole_report.json')); assert d['instructions_saved']>=1" | |
| grep -q "8" /tmp/peephole_out.s | |
| - name: Run Topic 17 register-allocation regressions | |
| run: | | |
| $PY -m pytest \ | |
| tests/test_regalloc_pseudo.py \ | |
| tests/test_regalloc_pseudo_benchmark.py \ | |
| tests/test_regalloc_p1.py \ | |
| tests/test_regalloc_metrics.py \ | |
| tests/test_regalloc_spill_compare.py \ | |
| tests/test_abi_frame.py \ | |
| tests/test_riscv_encoder_validation.py \ | |
| tests/test_rv32_emulator.py \ | |
| tests/test_simulator.py::TestRealProfiledMachine::test_lw_preserves_all_four_bytes \ | |
| -v --tb=short | |
| - name: Topic 21 CNN IR verification smoke test | |
| run: | | |
| mkdir -p benchmark_reports/ir_verifier_smoke | |
| for level in none basic all; do | |
| # Verify tensor IR without invoking the scalar-only legacy backend. | |
| $PY -m scratchv models/graph/cnn.onnx \ | |
| --backend ir --verify-ir --optimize "$level" --dump-ir \ | |
| -o "benchmark_reports/ir_verifier_smoke/cnn_${level}.ir" \ | |
| 2>&1 | tee "benchmark_reports/ir_verifier_smoke/cnn_${level}.log" | |
| test -s "benchmark_reports/ir_verifier_smoke/cnn_${level}.ir" | |
| done | |
| shell: bash | |
| - name: Generate test visualization page | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| $PY scratchv/ci/test_page.py \ | |
| --junit benchmark_reports/test_results.xml \ | |
| -o benchmark_reports/tests.html | |
| - name: Upload test reports | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: test-reports | |
| path: | | |
| benchmark_reports/test_results.xml | |
| benchmark_reports/ir_interpreter_tests.xml | |
| benchmark_reports/tests.html | |
| benchmark_reports/ir_verifier_smoke/ | |
| benchmark_reports/topic04_pass_manager.xml | |
| benchmark_reports/topic04_pass_manager.txt | |
| retention-days: 30 | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| # 模型性能测试:ONNX模型管线 + DSL用例 + CNN RISC-V编译 | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| benchmark: | |
| runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-latest' || 'self-hosted' }} | |
| timeout-minutes: 30 | |
| env: | |
| # Pin the scheduler's timing authority so both runners agree; see the | |
| # toolchain step below. | |
| LLVM_MCA: llvm-mca-18 | |
| steps: | |
| - name: Checkout pull request on isolated runner | |
| if: github.event_name == 'pull_request' | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python for pull request | |
| if: github.event_name == 'pull_request' | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Sync from local mirror (skip unstable GitHub checkout) | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| echo "=== CI Sync ===" | |
| retry() { | |
| local desc="$1" rc=0; shift | |
| for i in 1 2 3; do | |
| if "$@" --depth=1; then rc=0; break; fi | |
| rc=$? | |
| if [ "$i" -lt 3 ]; then | |
| echo "::warning::$desc failed, retry #$i in $((i*3))s" | |
| sleep $((i * 3)) | |
| else | |
| echo "::error::$desc failed after 3 attempts" | |
| fi | |
| done | |
| return $rc | |
| } | |
| MIRROR=/opt/ScratchV | |
| WORKSPACE=/opt/actions-runner/_work/ScratchV/ScratchV | |
| rm -rf "$WORKSPACE" | |
| cp -a "$MIRROR" "$WORKSPACE" | |
| cd "$WORKSPACE" | |
| echo "Fetching origin main..." | |
| retry "git fetch origin main" git fetch origin main || exit 1 | |
| echo "Checking out $GITHUB_SHA..." | |
| if ! git -c advice.detachedHead=false checkout -f "$GITHUB_SHA"; then | |
| echo "::warning::Checkout failed, fetching SHA directly from origin..." | |
| git fetch origin "$GITHUB_SHA" || true | |
| if git -c advice.detachedHead=false checkout -f "$GITHUB_SHA"; then | |
| echo "::notice::Checkout successful after direct fetch: $(git log -1 --format='%h %ai %s')" | |
| else | |
| echo "::error::GITHUB_SHA ($GITHUB_SHA) not found — checkout failed" | |
| exit 1 | |
| fi | |
| else | |
| echo "::notice::Checkout successful: $(git log -1 --format='%h %ai %s')" | |
| fi | |
| - name: Install dependencies | |
| run: | | |
| set -euo pipefail | |
| if python -c 'import sys; sys.exit(0 if sys.version_info[:2] == (3, 12) else 1)'; then | |
| PY=python | |
| elif command -v python3.12 >/dev/null 2>&1; then | |
| PY=python3.12 | |
| else | |
| echo "Python 3.12 not found" >&2 | |
| exit 1 | |
| fi | |
| echo "PY=$PY" >> "$GITHUB_ENV" | |
| echo "Using python: $($PY -c 'import sys; print(sys.executable, sys.version)')" | |
| $PY -m pip install --upgrade pip setuptools wheel | |
| $PY -m pip install -e ".[all]" | |
| $PY -m pip install markdown "pytest>=7,<10" | |
| # Topic 18 reports share the existing benchmark job and report artifact. | |
| - name: IR interpreter execution benchmark | |
| timeout-minutes: 5 | |
| env: | |
| PYTHONHASHSEED: "0" | |
| OMP_NUM_THREADS: "1" | |
| OPENBLAS_NUM_THREADS: "1" | |
| MKL_NUM_THREADS: "1" | |
| run: | | |
| $PY -m benchmarks.bench_ir_interpreter --warmup 1 --repeats 3 \ | |
| --json-output benchmark_reports/ir_interpreter.json \ | |
| --markdown benchmark_reports/ir_interpreter.md | |
| - name: Prepare RISC-V scheduling execution tools | |
| run: | | |
| set -euo pipefail | |
| # llvm-mca is the scheduler's timing authority. The checked-in benchmark | |
| # numbers and the scheduler test expectations are calibrated to LLVM 18. | |
| # Ubuntu's `llvm` metapackage resolves to 14 on 22.04 and 18 on 24.04, so | |
| # pin the major version instead of taking whatever `llvm` happens to be. | |
| if ! command -v clang >/dev/null || ! command -v ld.lld >/dev/null \ | |
| || ! command -v qemu-riscv32 >/dev/null || ! command -v llvm-mca-18 >/dev/null; then | |
| # The self-hosted runner has no passwordless sudo; there the toolchain is | |
| # provisioned out of band and the checks below are the assertion. | |
| if sudo -n true 2>/dev/null; then | |
| sudo apt-get update | |
| sudo apt-get install --no-install-recommends -y clang lld qemu-user llvm-18 \ | |
| || echo "::warning::llvm-18 is not available from this runner's repositories" | |
| else | |
| echo "::warning::no passwordless sudo; using the runner's preinstalled toolchain" | |
| fi | |
| fi | |
| missing="" | |
| for tool in clang ld.lld qemu-riscv32 llvm-mca-18; do | |
| command -v "$tool" >/dev/null 2>&1 || missing="$missing $tool" | |
| done | |
| if [ -n "$missing" ]; then | |
| echo "::error::missing tools:$missing -- provision them on this runner as root; CI cannot install them (no passwordless sudo)" | |
| echo "::notice::Ubuntu 22.04's default \`llvm\` package is 14, so \`llvm-mca-18\` needs the apt.llvm.org archive; the scheduler's expectations are calibrated to LLVM 18 (docs/topics/18-指令调度器/README.md)." | |
| exit 1 | |
| fi | |
| $PY -c "from scratchv.backend.llvm_mca import check_version; v = check_version('llvm-mca-18'); print(v); assert 'LLVM version 18.' in v, 'scheduler timing requires LLVM 18, got: ' + v" | |
| - name: Topic 9 DSL diagnostics benchmark | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }} | |
| run: | | |
| if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then | |
| echo "::error::No baseline commit available for DSL comparison." | |
| exit 1 | |
| fi | |
| git fetch origin "$BASE_SHA" --depth=1 | |
| BASELINE=$(mktemp -d "$RUNNER_TEMP/dsl-baseline.XXXXXX") | |
| git worktree add --detach "$BASELINE" "$BASE_SHA" | |
| trap 'git worktree remove --force "$BASELINE"' EXIT | |
| python3.12 -m benchmarks.bench_dsl_diagnostics \ | |
| --baseline-root "$BASELINE" \ | |
| --allow-input-registration \ | |
| --allow-ir-change 016_if_simple.dsl \ | |
| --allow-ir-change 017_while_sum.dsl \ | |
| --allow-ir-change 018_nested_if.dsl \ | |
| --allow-ir-change 021_dsl_if_else.dsl \ | |
| --allow-ir-change 022_dsl_while_sum.dsl \ | |
| --json-output benchmark_reports/dsl_diagnostics.json \ | |
| --markdown benchmark_reports/dsl_diagnostics.md \ | |
| --html benchmark_reports/dsl_diagnostics.html | |
| - name: Topic 01 DSL frontend benchmark | |
| run: | | |
| $PY -m benchmarks.bench_dsl_control_flow \ | |
| --repeats 20 \ | |
| --json-output benchmark_reports/topic01_control_flow.json \ | |
| --markdown benchmark_reports/topic01_control_flow.md \ | |
| --html benchmark_reports/topic01_control_flow.html | |
| # ── 3.1 ONNX 模型管线基准测试 ────────────────────────────────────── | |
| - name: ONNX model pipeline benchmarks | |
| run: $PY -m pytest benchmarks/test_benchmark.py -v --tb=short | |
| - name: Topic 21 CNN compilation with IR verification | |
| run: | | |
| $PY -m benchmarks.bench_ir_verifier \ | |
| --model models/graph/cnn.onnx --warmup 1 --repeats 5 \ | |
| --json-output benchmark_reports/ir_verifier.json \ | |
| --markdown benchmark_reports/ir_verifier.md | |
| # ── 3.1.1 课题14:固定 case + 真实 TinyFive A/B 报告 ───────────── | |
| - name: Topic 14 constant-merge case report | |
| run: | | |
| $PY benchmarks/run_const_merge_case.py \ | |
| benchmarks/cases/const_merge_feature.asm \ | |
| --json benchmark_reports/const_merge_report.json \ | |
| --markdown benchmark_reports/const_merge_report.md | |
| # ── 课题18:固定用例执行验证与独立合成规模测试 ───────────────── | |
| - name: Topic 18 scheduling case report | |
| run: | | |
| $PY -m benchmarks.run_inst_scheduler_case \ | |
| --json benchmark_reports/inst_scheduler_report.json \ | |
| --markdown benchmark_reports/inst_scheduler_report.md | |
| - name: Topic 18 synthetic scheduling benchmark | |
| run: | | |
| $PY -m benchmarks.bench_inst_scheduler --repeats 3 \ | |
| --json benchmark_reports/inst_scheduler_synthetic.json \ | |
| --markdown benchmark_reports/inst_scheduler_synthetic.md | |
| # ── 3.1.2 课题13:窥孔前后对比报告 ───────────────────────────── | |
| - name: Topic 13 peephole compare report | |
| run: make -s ci-peephole | |
| # ── 3.2 DSL 用例编译 + 模拟基准 ──────────────────────────────────── | |
| - name: DSL case compilation benchmarks | |
| run: | | |
| mkdir -p benchmark_reports | |
| $PY benchmarks/bench_runner.py benchmarks/cases \ | |
| --output-json benchmark_reports/dsl_bench.json \ | |
| --output-html benchmark_reports/dsl_bench.html \ | |
| --output-md benchmark_reports/dsl_bench.md | |
| # ── 3.3 CNN RISC-V 编译 + 性能估算 ──────────────────────────────── | |
| - name: CNN RISC-V compilation & estimation | |
| run: | | |
| mkdir -p benchmark_reports models/graph | |
| MODEL=models/graph/cnn.onnx | |
| test -f "$MODEL" | |
| $PY -m scratchv.standalone.onnx_to_riscv_standalone \ | |
| "$MODEL" -o /tmp/cnn_riscv.bin \ | |
| --asm benchmark_reports/cnn_scratchv.s \ | |
| --estimate --report --const-merge | |
| - name: Topic 18 CNN scheduling benefit and side effects | |
| env: | |
| PYTHONHASHSEED: "0" | |
| run: | | |
| $PY -m benchmarks.bench_cnn_schedule \ | |
| --model models/graph/cnn.onnx \ | |
| --standalone-asm benchmark_reports/cnn_scratchv.s \ | |
| --llvm-mca llvm-mca-18 \ | |
| --json benchmark_reports/inst_scheduler_cnn.json \ | |
| --markdown benchmark_reports/inst_scheduler_cnn.md | |
| # ── 3.3.1 汇编美化器:真实 CNN 输出 + Actions Summary ─────────── | |
| - name: CNN assembly beautifier benchmark | |
| run: | | |
| $PY -m benchmarks.bench_asm_beautifier \ | |
| --input benchmark_reports/cnn_scratchv.s \ | |
| --output-asm benchmark_reports/cnn_pretty.s \ | |
| --markdown benchmark_reports/asm_beautifier_summary.md \ | |
| --json benchmark_reports/asm_beautifier_summary.json \ | |
| --repeats 20 | |
| # ── 3.4 LLVM vs ScratchV + TinyFive + Dashboard ────────────────── | |
| - name: LLVM vs ScratchV comparison | |
| run: | | |
| mkdir -p benchmark_reports | |
| # Generate ScratchV assembly if not already present | |
| if [ ! -f benchmark_reports/cnn_scratchv.s ] && [ -f models/graph/cnn.onnx ]; then | |
| $PY scratchv/standalone/onnx_to_riscv_standalone.py \ | |
| models/graph/cnn.onnx -o /tmp/cnn_riscv.bin \ | |
| --asm benchmark_reports/cnn_scratchv.s --estimate \ | |
| --const-merge 2>&1 | tail -3 | |
| fi | |
| $PY scratchv/standalone/llvm_cache_compare.py \ | |
| --json-output benchmark_reports/llvm_vs_scratchv.json \ | |
| --markdown benchmark_reports/llvm_vs_scratchv.md \ | |
| || echo "WARNING: llvm_cache_compare failed" | |
| $PY scratchv/standalone/tinyfive_compare.py \ | |
| --json benchmark_reports/tinyfive_compare.json \ | |
| --markdown benchmark_reports/tinyfive_compare.md \ | |
| --scratchv-asm benchmark_reports/cnn_scratchv.s \ | |
| || echo "WARNING: tinyfive_compare failed" | |
| # ── 3.5 ONNX 模型拆分为单算子 ──────────────────────────────────── | |
| - name: Split CNN model into single operators | |
| run: | | |
| $PY scripts/split_cnn_to_single_ops.py \ | |
| || echo "WARNING: split_cnn_to_single_ops failed" | |
| # ── 3.6 单算子 benchmark ──────────────────────────────────────── | |
| - name: Single-operator benchmarks | |
| run: | | |
| $PY scripts/bench_single_ops.py \ | |
| --output-json benchmark_reports/single_op_bench.json \ | |
| --output-md benchmark_reports/single_op_bench.md \ | |
| --pressure-regs 2,3,5,8,12,19 | |
| # ── 3.7 生成仪表盘 HTML (版本vsLLVM + 指令分类 + 算子对比) ───── | |
| - name: Generate dashboard | |
| run: | | |
| $PY scratchv/ci/dashboard.py \ | |
| --llvm-json benchmark_reports/llvm_vs_scratchv.json \ | |
| --history-json benchmark_reports/optimization_history.json \ | |
| -o benchmark_reports/dashboard.html \ | |
| || echo "WARNING: dashboard generation failed" | |
| # ── 3.8 寄存器分配 benchmark(线性扫描:simple/dense/cnn + LLVM对比) ── | |
| - name: Topic 17 register-allocation before/after benchmark | |
| run: | | |
| $PY -m benchmarks.test_regalloc.bench_regalloc_linear \ | |
| --repeats 30 \ | |
| --output-json benchmark_reports/regalloc_bench.json \ | |
| --output-html benchmark_reports/regalloc_bench.html \ | |
| --output-md benchmark_reports/regalloc_bench.md | |
| - name: Topic 17 spill-pressure comparison | |
| run: | | |
| $PY -m benchmarks.bench_regalloc_spill_compare \ | |
| --json-output benchmark_reports/regalloc_spill_compare.json \ | |
| --markdown benchmark_reports/regalloc_spill_compare.md | |
| # ── 上报 ────────────────────────────────────────────────────────── | |
| - name: Upload benchmark reports | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: benchmark-reports | |
| path: | | |
| benchmark_reports/ | |
| !benchmark_reports/peephole_benchmark.html | |
| !benchmark_reports/peephole_compare.json | |
| !benchmark_reports/peephole_compare_html.json | |
| !benchmark_reports/peephole_raw.json | |
| !benchmark_reports/cnn_peephole_compare.json | |
| !benchmark_reports/peephole_summary.md | |
| retention-days: 30 | |
| - name: Upload peephole benchmark report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: peephole-benchmark-report | |
| path: | | |
| benchmark_reports/peephole_benchmark.html | |
| benchmark_reports/peephole_compare.json | |
| benchmark_reports/peephole_compare_html.json | |
| benchmark_reports/peephole_raw.json | |
| benchmark_reports/cnn_peephole_compare.json | |
| benchmark_reports/peephole_summary.md | |
| if-no-files-found: error | |
| retention-days: 30 | |
| # ── 保留推广主页(docs/promo/ScratchV.html 不会被顶替) ─────────── | |
| - name: Copy promotional page into Pages artifact | |
| if: github.ref == 'refs/heads/main' | |
| run: cp docs/promo/ScratchV.html benchmark_reports/ScratchV.html | |
| # ── 文档课程 HTML 站点 ────────────────────────────────────────────── | |
| - name: Build docs HTML course site | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| $PY -m pip install markdown | |
| $PY scripts/build_docs_html.py --output-dir benchmark_reports/docs | |
| $PY scripts/check_docs_links.py --html-dir benchmark_reports/docs | |
| echo "Docs site built: benchmark_reports/docs/index.html" | |
| # ── 优化历史页面 ────────────────────────────────────────────────── | |
| - name: Generate optimization history page | |
| if: github.ref == 'refs/heads/main' | |
| run: $PY scratchv/ci/history_page.py -o benchmark_reports/history.html | |
| # ── 测试可视化页面 ──────────────────────────────────────────────── | |
| - name: Generate test visualization page | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| $PY scratchv/ci/test_page.py \ | |
| --junit benchmark_reports/test_results.xml \ | |
| -o benchmark_reports/tests.html | |
| # ── 精简 Pages 产物(移除大型编译产物) ────────────────────────── | |
| - name: Clean large binaries before Pages deploy | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| rm -f benchmark_reports/*.bin benchmark_reports/*.elf | |
| rm -f benchmark_reports/*.ll | |
| rm -f benchmark_reports/cnn.onnx_* | |
| rm -f benchmark_reports/_ll_* benchmark_reports/_sv.* | |
| echo "Cleaned large binaries from Pages artifact" | |
| # ── 上传 Pages 产物 ────────────────────────────────────────────── | |
| - name: Upload Pages artifact | |
| if: github.ref == 'refs/heads/main' | |
| uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 | |
| with: | |
| path: benchmark_reports/ | |
| - name: Write job summary | |
| if: always() | |
| run: | | |
| if [ -f benchmark_reports/ir_interpreter.md ]; then | |
| cat benchmark_reports/ir_interpreter.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| if [ -f benchmark_reports/topic01_control_flow.md ]; then | |
| cat benchmark_reports/topic01_control_flow.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| if [ -f benchmark_reports/dsl_diagnostics.md ]; then | |
| cat benchmark_reports/dsl_diagnostics.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| if [ -f benchmark_reports/const_merge_report.md ]; then | |
| cat benchmark_reports/const_merge_report.md >> $GITHUB_STEP_SUMMARY | |
| fi | |
| for report in inst_scheduler_report inst_scheduler_cnn inst_scheduler_synthetic; do | |
| if [ -f "benchmark_reports/$report.md" ]; then | |
| cat "benchmark_reports/$report.md" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/peephole_summary.md ]; then | |
| cat benchmark_reports/peephole_summary.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/github_summary.md ]; then | |
| cat benchmark_reports/github_summary.md >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/asm_beautifier_summary.md ]; then | |
| cat benchmark_reports/asm_beautifier_summary.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "### LLVM vs ScratchV" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/llvm_vs_scratchv.md ]; then | |
| tail -20 benchmark_reports/llvm_vs_scratchv.md >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Topic 17 Register Allocation" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/regalloc_bench.md ]; then | |
| cat benchmark_reports/regalloc_bench.md >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f benchmark_reports/regalloc_spill_compare.md ]; then | |
| cat benchmark_reports/regalloc_spill_compare.md >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| if [ -f benchmark_reports/single_op_bench.md ]; then | |
| cat benchmark_reports/single_op_bench.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| if [ -f benchmark_reports/ir_verifier.md ]; then | |
| cat benchmark_reports/ir_verifier.md >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| # GitHub Pages 部署 (仅 main 分支) | |
| # ═════════════════════════════════════════════════════════════════════════ | |
| deploy-pages: | |
| needs: benchmark | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 |