Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ jobs:
command -v clang
command -v ld.lld
command -v qemu-riscv32
clang --version
ld.lld --version
qemu-riscv32 --version
$PY -c 'from scratchv.backend.llvm_mca import check_version; print(check_version("llvm-mca"))'

# Includes scheduling and standalone CNN execution regressions.
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ docs/

## 代码风格

- Python 3.12+, type hints
- Python 3.8+ compatible, type hints;版本与注解规则以 `CONTRIBUTING.md` 为准。
- 测试沿用现有 CI 流程;按用户要求移除 Python 3.8 专项测试。项目最低版本仍为 Python 3.8,代码必须兼容该版本。
- argparse CLI: `--json` (bool, stdout), `--json-output` (path), `--markdown` (path)
- 报告工具: HTML/JSON/MD 三种格式,零外部可视化依赖
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**从 ONNX 到 RISC-V 机器码 — 一个你完全看得懂的 AI 编译器。**

[![Python](https://img.shields.io/badge/python-3.12+-blue)](https://python.org)
[![Python](https://img.shields.io/badge/python-3.8+-blue)](https://python.org)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![CI](https://github.com/ScratchV-Compiler/ScratchV/actions/workflows/ci.yml/badge.svg)](https://github.com/ScratchV-Compiler/ScratchV/actions)
[![Docs](https://img.shields.io/badge/docs-课程站点-blue)](https://scratchv-compiler.github.io/ScratchV/docs/)
Expand Down
6 changes: 5 additions & 1 deletion benchmarks/bench_cnn_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,16 @@ def run_benchmark(model_path: Path = DEFAULT_MODEL, *, standalone_asm: Path | No
}
if any(value is False for value in primary["acceptance"].values()):
primary["status"] = "failed"
try:
input_path = model_path.relative_to(ROOT).as_posix()
except ValueError:
input_path = str(model_path)
return {
"schema_version": 4, "benchmark_type": "cnn-scheduling-ab",
"status": "passed" if all(c["status"] == "passed" for c in cases) else "failed",
"validation_scope": "static_and_llvm" + ("_and_execution" if execute else ""),
"whole_cnn_execution": primary["execution"]["status"],
"input": {"path": model_path.relative_to(ROOT).as_posix() if model_path.is_relative_to(ROOT) else str(model_path),
"input": {"path": input_path,
"sha256": sha256(model_bytes), "bytes": len(model_bytes), "nodes": len(model.graph.node),
"operators": dict(sorted(Counter(n.op_type for n in model.graph.node).items()))},
"environment": {"python": platform.python_version(), "onnx": onnx.__version__,
Expand Down
8 changes: 6 additions & 2 deletions benchmarks/bench_dsl_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ def parse_worker(args: argparse.Namespace) -> dict:
from scratchv.frontend import dsl_extended

parser_file = Path(dsl_extended.__file__).resolve()
if not parser_file.is_relative_to(args.worker_root):
raise RuntimeError(f"parser imported outside selected checkout: {parser_file}")
try:
parser_file.relative_to(args.worker_root)
except ValueError:
raise RuntimeError(
f"parser imported outside selected checkout: {parser_file}"
) from None
paths = sorted(args.cases.glob("*.dsl"))
if not paths:
raise ValueError(f"no DSL cases in {args.cases}")
Expand Down
6 changes: 4 additions & 2 deletions benchmarks/compare_schedule_disabled.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ def collect(root: Path) -> dict:
from scratchv.standalone.onnx_to_riscv_standalone import convert_onnx_to_riscv
from scripts.run_topic06_benchmarks import run_simulation

if not Path(compiler.__file__).resolve().is_relative_to(root):
raise RuntimeError("Compiler imported outside the selected checkout")
try:
Path(compiler.__file__).resolve().relative_to(root)
except ValueError:
raise RuntimeError("Compiler imported outside the selected checkout") from None
cases = sorted((root / "benchmarks/cases").glob("*.dsl"))
cases += sorted((root / "tests/topic06/cases").rglob("*.dsl"))
cases.append(root / "models/graph/cnn.onnx")
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/generate_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Supported ops: Add, Mul, Sub, Div, Relu, MatMul, MaxPool, GeLU, Softmax, Neg, Exp
"""

from __future__ import annotations

import os

import numpy as np
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/test_regalloc/bench_cnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
verifies execution via the RV32 emulator.
"""

from __future__ import annotations

import argparse
import os
import re
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/test_regalloc/bench_dense.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
registers than physical registers.
"""

from __future__ import annotations

import argparse
import os
import random
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/test_regalloc/bench_regalloc_linear.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
"""Run all register allocation benchmarks and produce a report."""

from __future__ import annotations

import argparse
import datetime
import json
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/test_regalloc/bench_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
physical registers are plentiful.
"""

from __future__ import annotations

import argparse
import os
import random
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/test_regalloc/bench_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# flake8: noqa
"""Common used benchmark utils for all benchmarks"""

from __future__ import annotations

# Common used constants
_CALLEE_SAVED = {
"ra",
Expand Down
4 changes: 4 additions & 0 deletions benchmarks/topic28/f32_sum.dsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
a = const(1.5, dtype:f32)
b = const(2.0, dtype:f32)
result = add(a, b)
return result
8 changes: 8 additions & 0 deletions benchmarks/topic28/f64_dot.dsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
a = const(1.5, dtype:f64)
b = const(2.0, dtype:f64)
c = const(3.0, dtype:f64)
d = const(4.0, dtype:f64)
left = mul(a, b)
right = mul(c, d)
result = add(left, right)
return result
6 changes: 6 additions & 0 deletions benchmarks/topic28/f64_sum.dsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
a = const(1.5, dtype:f64)
b = const(2.0, dtype:f64)
c = const(3.25, dtype:f64)
partial = add(a, b)
result = add(partial, c)
return result
6 changes: 6 additions & 0 deletions benchmarks/topic28/i32_div_rem.dsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
a = const(-17, dtype:i32)
b = const(5, dtype:i32)
quotient = div(a, b)
remainder = rem(a, b)
result = add(quotient, remainder)
return result
60 changes: 60 additions & 0 deletions docs/topics/28-扩展指令选择/28-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Topic 28 使用指南

本课题完成整数算子扩展及标量 f32/f64 的 RISC-V F/D 指令选择。交付与完成条件见[交付与验收](28-交付与验收.md),详细合同见[技术设计](28-扩展指令选择-设计文档.md)。

## Python 版本

项目最低支持 Python 3.8,与安装配置一致,代码必须兼容该版本。现有 CI 使用 Python 3.12 执行功能测试;这是测试环境,不改变项目的最低版本要求。按用户要求移除 Python 3.8 专项测试。

## 支持的操作和类型

所有新增能力通过 `--extended-isel` 启用。

| 操作 | i32 | f32 | f64 |
|---|---|---|---|
| `const/add/sub/mul/div/neg` | RV32IM 整数指令 | F 扩展 `.s` | D 扩展 `.d` |
| `rem/mod` | 有符号 `rem`;`mod` 为别名 | 不支持 | 不支持 |
| `min/max/abs` | 整数指令序列 | `fmin.s/fmax.s/fabs.s` | `fmin.d/fmax.d/fabs.d` |
| `sqrt` | 不支持 | `fsqrt.s` | `fsqrt.d` |
| IR `load/store` 与 spill | `lw/sw`,4 字节 | `flw/fsw`,4 字节 | `fld/fsd`,8 字节 |
| 比较 `== != < > <= >=`、条件分支 | GPR 比较/分支 | FPR 比较,GPR 布尔结果 | FPR 比较,GPR 布尔结果 |
| 单值返回 | `a0` | `fa0` | `fa0` |

DSL 常量写成 `const(1.5, dtype:f64)`;省略 dtype 时为 f32。IR 的类型系统位于 `scratchv/ir/types.py`,以 `DataType.FLOAT32/FLOAT64` 区分两种精度,仓库没有名为 `type_system.py` 的模块。ONNX 标量 `Add/Sub/Mul/Div/Sqrt/Min/Max` 保留 FLOAT/DOUBLE 类型。

| ISA | ABI | 类型 |
|---|---|---|
| `rv32im` | `ilp32` | i32 |
| `rv32imf` | `ilp32f` | i32、f32 |
| `rv32imfd` | `ilp32d` | i32、f32、f64 |

`naive/greedy/linear` 三种分配器均支持 GPR/FPR 分类分配和按类型 spill。不同 dtype 不做隐式转换;不支持的 ISA/ABI/type 组合明确报错。

## 示例与验收

在已安装项目依赖的 Python 环境中,从仓库根目录运行:

```bash
python -m scratchv.main benchmarks/topic28/i32_div_rem.dsl \
--extended-isel --target-isa rv32im --target-abi ilp32 \
--reg-alloc greedy -o /tmp/topic28_i32.s
python -m scratchv.main benchmarks/topic28/f64_sum.dsl \
--extended-isel --target-isa rv32imfd --target-abi ilp32d \
--reg-alloc linear -o /tmp/topic28_f64.s
clang --target=riscv32 -march=rv32imfd -mabi=ilp32d \
-c /tmp/topic28_f64.s -o /tmp/topic28_f64.o

SCRATCHV_REQUIRE_RISCV_EXECUTION=1 python -m pytest -q tests/test_topic28_acceptance.py
```

四个交付示例分别是 [i32 除法和余数](../../../benchmarks/topic28/i32_div_rem.dsl)(−5)、[f32 求和](../../../benchmarks/topic28/f32_sum.dsl)(3.5)、[f64 求和](../../../benchmarks/topic28/f64_sum.dsl)(6.75)、[f64 点积](../../../benchmarks/topic28/f64_dot.dsl)(15)。验收测试自动汇编、链接并通过 QEMU 执行,无需 RISC-V C 运行库。工具与完整回归命令见[验收说明](28-交付与验收.md#验收方法)。

## 数值语义与边界

- 整数除法向零截断,余数与被除数同号:`div(-17,5)=-3`,`mod(-17,5)=-2`。除零及 `INT32_MIN/-1` 使用 RV32M 定义,不插入陷阱。
- 浮点算术和平方根固定使用 `rne`(最近值、相同距离取偶数)。DSL 的 `min/max` 使用 RISC-V 的 NaN、符号零及异常标志语义,不能用 Python `min/max` 直接替代。
- ONNX 浮点 Min/Max 保留 NaN:任意输入为 NaN,结果就是 NaN,变长输入逐对保留这条规则;运算产生规范化的 quiet NaN,sNaN 设置 invalid 标志。非 NaN 输入继续使用 RISC-V 的符号零规则(Min 选择 −0,Max 选择 +0);本次 NaN 修复不改变这项既有目标约定。
- 硬浮点交付产物是合法 F/D 汇编,执行验证使用 QEMU;内置 encoder/emulator 目前仅支持 RV32IM。
- ONNX 硬浮点入口处理标量,支持至多一个标量浮点参数;每个函数的栈帧上限为 2032 字节。
- ONNX initializer 与 graph input 同名时,当前接口冻结 initializer,不生成可覆盖参数;不提供运行时覆盖。这是既有入口政策,ONNX 规范允许实现选择是否提供覆盖。
- `--extended-isel` 与 `--dag-isel`、LLVM 后端互斥。生产 linear 分配器为 `regalloc_linear.py`。
52 changes: 52 additions & 0 deletions docs/topics/28-扩展指令选择/28-交付与验收.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Topic 28 交付与验收

更新:2026-10-14。交付按[原始任务](28-扩展指令选择.md)的六项要求组织。

## 要求与交付对应

| 原始要求 | 交付位置 | 验收依据 |
|---|---|---|
| 1. 分析已有算子映射 | [基础选择器](../../../scratchv/backend/instruction_select.py)、[设计](28-扩展指令选择-设计文档.md)、[支持列表](28-README.md#支持的操作和类型) | 原有整数路径及扩展映射有明确说明 |
| 2–3. div/mod/sqrt/min/max 映射 | [扩展选择器](../../../scratchv/backend/inst_select_ext.py)、[目标校验](../../../scratchv/backend/target_features.py) | i32 div/rem 与 F/D 数学指令真实执行 |
| 4. f64 寄存器、访存、算术 | [机器类型](../../../scratchv/backend/machine_types.py)、[分类分配](../../../scratchv/backend/allocation.py)、[栈帧/ABI](../../../scratchv/backend/abi_frame.py) | FPR、8 字节槽、fld/fsd、四则算术 |
| 5. IR 区分 f32/f64 | [类型系统](../../../scratchv/ir/types.py)、[IRBuilder](../../../scratchv/ir/builder.py)、[DSL](../../../scratchv/frontend/dsl_parser.py)、[ONNX](../../../scratchv/frontend/onnx_parser.py) | dtype 和 FLOAT/DOUBLE 贯通 |
| 6. 测试算子与类型 | [验收测试](../../../tests/test_topic28_acceptance.py)、[示例](../../../benchmarks/topic28) | 三种分配器,实际 ELF 执行结果和 ABI 检查 |
| 支持列表、类型与使用说明 | [使用指南](28-README.md)、[设计](28-扩展指令选择-设计文档.md) | 操作、类型、ISA/ABI 与运行命令 |

原任务所称 type_system.py 对应本仓库现有的 `scratchv/ir/types.py`。

W1–W4 对应整数映射和边界;W5–W8 对应 F/D 类型、访存和四则算术;W9 对应 f64 六种比较及条件分支;W10 对应双精度求和、点积和 QEMU 验证;W11 采用硬件 sqrt/min/max;W12 对应使用文档。

## 验收方法

在已安装项目和测试依赖的 Python 环境中运行,PATH 需包含支持 RISC-V 的 Clang、LLD、qemu-riscv32;全量回归还需 readelf。

代码必须兼容 Python 3.8。功能回归沿用现有 CI 测试任务(解释器为 Python 3.12),并上传到 test-reports artifact;测试环境版本不改变项目的最低支持版本。

```bash
SCRATCHV_REQUIRE_RISCV_EXECUTION=1 python -m pytest -q tests/test_topic28_acceptance.py
SCRATCHV_REQUIRE_RISCV_EXECUTION=1 python -m pytest -q tests benchmarks/test_benchmark.py
```

验收入口为 99 项:四个交付 DSL 程序;整数商/余数、除零和溢出;DSL/ONNX 标量数学;F-only、D 上 f32、D 上 f64;f64 访存及六种比较的两条分支。结果以实际 ELF 返回的位模式为准,并检查 fflags、调用后的 sp/ra、GPR/FPR callee-saved 状态和 f32 NaN-boxing。

与算子、类型、合法汇编和调用正确性有关的回归保留,完整回归检查共享编译管线兼容性。


## 测试保留范围

测试按本课题新增行为和实际修改的共享模块保留。Review 编号用于追溯缺陷;后续新增测试需对应下列合同,不再对每个场景重复展开全部配置。

| 保留层面 | 测试位置 | 展开原则 |
|---|---|---|
| 原始任务验收 | [acceptance](../../../tests/test_topic28_acceptance.py) | 99 项完整保留,覆盖三种分配器、整数和 F/D 目标、交付程序及真实 ABI |
| 算子映射、类型传播与拒绝边界 | [IR/前端](../../../tests/test_topic28_ir_frontend.py)、[整数选择](../../../tests/test_topic28_isel_integer.py)、[浮点选择](../../../tests/test_topic28_stage2.py)、[CLI](../../../tests/test_topic28_cli_integration.py)、[整数折叠](../../../tests/test_topic28_constant_folding.py)、[选择器单测](../../../tests/test_inst_select_ext.py) | 保留操作、类型、合法/非法输入;前端拒绝不重复乘上分配器和优化级别 |
| FPR 分配、spill、物理寄存器及混合调用 | [分配合同](../../../tests/test_topic28_stage3.py)、[物理定义](../../../tests/test_topic28_review_backend.py)、[固定源](../../../tests/test_topic28_review_fixed_uses.py)、[真实调用](../../../tests/test_topic28_fourth_review.py) | 分配器相关场景保留 naive/greedy/linear;单测检查槽宽度/对齐,ELF 检查返回位和调用状态 |
| DSL/ONNX 类型、常量与标量形状 | [输入边界](../../../tests/test_topic28_independent_boundaries.py)、[整数链](../../../tests/test_topic28_independent_integer.py)、[ONNX](../../../tests/test_topic28_review_onnx.py)、[原始编码](../../../tests/test_topic28_fifth_review.py)、[存储判别](../../../tests/test_topic28_sixth_review.py) | 保留输入形态、f32/f64、packed/unpacked/raw/external、重复字段等实际缺陷变体;编码问题采用代表编译配置 |
| 优化后的值、精度、fflags 和控制流 | [浮点优化](../../../tests/test_topic28_review_optimizations.py)、[DCE](../../../tests/test_topic28_overall_review.py)、[CFG/LICM](../../../tests/test_topic28_third_review.py) | 保留 none/basic/all 和显式重复 pass;浮点精度主矩阵保留三种分配器 × 三种级别;其他优化问题采用代表分配器 |
| 合法汇编、符号、函数大小及每函数栈帧 | [第四轮](../../../tests/test_topic28_fourth_review.py)、[第五轮](../../../tests/test_topic28_fifth_review.py)、[第六轮](../../../tests/test_topic28_sixth_review.py) | 保留符号、section、两条返回路径及帧大小边界;发射器自身问题不重复乘上优化配置 |
| 受影响的共享参照执行器 | [除法](../../../tests/test_topic28_independent_division.py)、[浮点参照](../../../tests/test_topic28_review_frontend_oracles.py) | 保留类型和逐位结果;数组用例仅检查本课题修改的参照函数兼容性,不表示后端支持张量计算 |

verifier 开关的两条路径保留在拒绝边界测试中;正确执行用例采用固定开关。三个重复的编译/模拟调用用例已合并进原始验收和真实混合调用回归,额外的重复汇编步骤已删除,执行帮助函数仍负责汇编、链接和运行。

本次课题相关参数项由 5759 精简为 1793,保留用例的输入、类型、编码和预期值组合未删减。其他课题既有的 1682 项测试保持原样,完整仓库回归共 3475 项。
Loading
Loading