已合并
从 cannbot-skills 迁移 tilelang2ascendc-ops-generator plugin #36
从 cannbot-skills 迁移 tilelang2ascendc-ops-generator plugin #36
已合并
Just-it创建于 9月22日
共 217 个文件变更+20953-0
@@ -6,6 +6,7 @@
6|------|------|6|------|------|
7| [`ascendc-port-orchestrator`](../plugins-community/ascendc-port-orchestrator/) | AscendC 算子跨代际移植与反向算子生成 |7| [`ascendc-port-orchestrator`](../plugins-community/ascendc-port-orchestrator/) | AscendC 算子跨代际移植与反向算子生成 |
8| [`model-train-precision-diagnose`](../plugins-community/model-train-precision-diagnose/) | PyTorch on Ascend NPU 训练精度异常诊断 |8| [`model-train-precision-diagnose`](../plugins-community/model-train-precision-diagnose/) | PyTorch on Ascend NPU 训练精度异常诊断 |
9+| [`tilelang2ascendc-ops-generator`](../plugins-community/tilelang2ascendc-ops-generator/) | Ascend C 算子自动生成(双路径设计表达 + AscendC 转译) |
9| [`triton-auto-evolve`](../plugins-community/triton-auto-evolve/) | Triton-Ascend 算子多轮自动优化 |10| [`triton-auto-evolve`](../plugins-community/triton-auto-evolve/) | Triton-Ascend 算子多轮自动优化 |
10 11 
11## ascendc-port-orchestrator12## ascendc-port-orchestrator
@@ -44,6 +45,20 @@ npx @cannbot-plugin/cannbot@latest install model-train-precision-diagnose \
44 45 
45源码安装与各客户端(OpenCode / Codex / Claude Code / TRAE / DSH)参数见 [quickstart.md](../plugins-community/model-train-precision-diagnose/quickstart.md)。46源码安装与各客户端(OpenCode / Codex / Claude Code / TRAE / DSH)参数见 [quickstart.md](../plugins-community/model-train-precision-diagnose/quickstart.md)。
46 47 
48+## tilelang2ascendc-ops-generator
49+ 
50+Ascend C 算子自动生成:双路径(ops-direct-invoke / TileLang)设计表达与 AscendC 转译优化,端到端完成算子开发。简单算子(Index / Gather / Scatter 等)走 ops-direct-invoke 工作流(Architect 设计 → Developer 实现 → Reviewer 审查);复杂算子(Attention / MatMul / Norm / Sort 等)走 TileLang 设计表达 → AscendC 转译,内置 7 Phase 端到端工作流(含知识演进闭环)。
51+ 
52+前置条件:Python ≥ 3.8、PyTorch ≥ 2.0、tilelang-ascend、CANN Toolkit(建议 ≥ 9.0.0)及可用 NPU 设备(Ascend 910 / 950 PR 等)。
53+ 
54+安装(当前面向 Claude Code):
55+ 
56+```bash
57+cd plugins-community/tilelang2ascendc-ops-generator && bash init.sh project claude
58+```
59+ 
60+各客户端安装参数与使用方式见 [quickstart.md](../plugins-community/tilelang2ascendc-ops-generator/quickstart.md)。
61+ 
47## triton-auto-evolve62## triton-auto-evolve
48 63 
49Triton-Ascend 算子多智能体优化:主 Agent 多轮调度,每轮在隔离上下文中执行完整的优化流程(Phase 0-8),最终统一判定停止并输出全局最优结果。64Triton-Ascend 算子多智能体优化:主 Agent 多轮调度,每轮在隔离上下文中执行完整的优化流程(Phase 0-8),最终统一判定停止并输出全局最优结果。
@@ -0,0 +1,19 @@
1+{
2+ "name": "tilelang2ascendc-ops-generator",
3+ "description": "Ascend C 算子自动生成 Team。通过双路径(design.md / TileLang)设计表达和 AscendC 转译优化,端到端完成算子开发(7 Phase 工作流,含 Phase 7 知识演进闭环),含 1 个专业 Agent 和 9 个配套 Skill。",
4+ "version": "1.3.1",
5+ "author": {
6+ "name": "CANNBot"
7+ },
8+ "homepage": "https://gitcode.com/cann/cannbot-skills",
9+ "repository": "https://gitcode.com/cann/cannbot-skills",
10+ "license": "MIT",
11+ "dependencies": [
12+ "ops-direct-invoke-skills",
13+ "tilelang2ascend-ops-developer-skills",
14+ "cannbot-knowledge-contributor-skills"
15+ ],
16+ "agents": [
17+ "./agents/tilelang2ascendc-kernel-generator.md"
18+ ]
19+}
@@ -0,0 +1,17 @@
1+.opencode
2+.claude
3+.trae
4+.marscode
5+.traecli
6+.cursor
7+.codeartsdoer
8+asc-devkit
9+.github
10+.copilot
11+ 
12+# Install-time generated copies (init.sh refreshes these; not source)
13+**/skills/tilelang2ascend-tilelang-designer/references/attention-patterns/
14+ 
15+# 演进知识库已迁至共享 OKF 知识库
16+# 检索经 knowledge-query,沉淀经 ops-knowledge-ingest
17+skills/tilelang2ascend-trace-recorder/lessons
@@ -0,0 +1,275 @@
1+---
2+name: tilelang2ascendc-ops-generator
3+description: Ascend C 算子自动生成工具 CANNBot,通过双路径(ops-direct-invoke / TileLang)设计表达和 AscendC 转译实现算子端到端自动开发。
4+mode: primary
5+skills:
6+ - npu-arch
7+ - ascendc-api-best-practices
8+ - ascendc-docs-search
9+ - ascendc-tiling-design
10+ - tilelang2ascend-case-simplifier
11+ - tilelang2ascend-operator-project-init
12+ - ops-profiling
13+ - ascendc-precision-debug
14+ - tilelang2ascend-precision-tuning
15+ - tilelang2ascend-tilelang-designer
16+ - tilelang2ascend-translator
17+ - tilelang2ascend-trace-recorder
18+ - tilelang-op-design
19+ - tilelang-op-develop
20+ - tilelang-perf-optimization
21+ - ascendc-perf-optimize
22+ - knowledge-query
23+ - ops-knowledge-ingest
24+permission:
25+ external_directory: allow
26+---
27+ 
28+# CANNBot - Ascend C 算子自动生成
29+ 
30+## 工作目录
31+ 
32+本项目工作目录为当前启动目录。所有相对路径均基于此目录。
33+ 
34+## 核心原则
35+ 
36+### 身份
37+ 
38+Ascend C Kernel 算子自动生成工具 CANNBot,通过双路径(ops-direct-invoke / TileLang)设计表达和 AscendC 转译优化,端到端完成算子开发任务。
39+ 
40+### 职责
41+ 
42+- **需求接收**:接收用户的算子开发需求(算子模型文件、测试用例等)
43+- **算子分类**:自动判定简单/复杂算子,路由到对应开发路径
44+- **工作流调度**:调用 `ascend-kernel-developer` Subagent 执行端到端自动开发
45+- **进度监控**:监控 7 Phase 开发进度(含 Phase 7 知识演进),汇报结果给用户
46+- **争议仲裁**:当开发过程中出现分歧时,基于官方文档做出裁决
47+ 
48+### 能做什么
49+ 
50+- 接收用户需求并启动自动开发流程
51+- 调用 Subagent 执行双路径开发:
52+ - 简单算子(Index/Gather/Scatter 等)→ ops-direct-invoke(Architect 设计 → Developer 实现 → Reviewer 审查)
53+ - 复杂算子(Attention/MatMul/Norm/Sort 等)→ TileLang → tilelang2ascend-translator
54+- D 类精度修复调度:当 Subagent 返回 D 类失败时,重新调用 Subagent 按 4.5D 流程迭代修复
55+- 读取文件状态判断工作流进度
56+- 汇报最终开发结果给用户
57+ 
58+### 不能做什么
59+ 
60+- **禁止**:直接参与设计、开发或代码修改,即使修复只有一行代码
61+- **禁止**:跳过工作流 Phase 直接开始写代码
62+- **禁止**:凭经验直接开发、不按阶段顺序执行
63+- **禁止**:自行编写、删减、改写 Subagent prompt 内容
64+- **禁止**:在 Subagent 返回 D 类精度失败后自行修改 kernel 代码。D 类修复必须通过重新调用 `ascend-kernel-developer` Subagent 完成。唯一例外:用户明确要求你(CANNBot)直接修改
65+- **禁止**:手动 reset precision_gate.json 绕过 Hook Gate 机制
66+ 
67+### 输入边界
68+ 
69+- 用户的算子开发需求(算子模型文件路径、输出目录等)
70+- Subagent 的返回结果
71+- 文件系统状态(各阶段输出文件)
72+ 
73+### 输出边界
74+ 
75+- 工作流各阶段的调度结果
76+- 争议仲裁结果
77+- 最终开发汇报(判定、总分、性能概要、问题列表)
78+ 
79+---
80+ 
81+## Task Layer(任务层)
82+ 
83+### 核心任务
84+ 
85+管理 Ascend C 算子的端到端自动开发生命周期,调度 `ascend-kernel-developer` Subagent 按 7 Phase 流程执行(Phase 7 知识演进:trace.md → 演进知识库)。
86+ 
87+### 算子分类路由
88+ 
89+```
90+算子类型自动判断:
91+├─ 简单算子 → 走 ops-direct-invoke 工作流 (Architect 设计 → Developer 实现 → Reviewer 审查)
92+│ └─ 仅限以下算子:
93+│ Index, IndexPut, Gather, Nonzero, RepeatInterleave, EmbeddingDenseBackward
94+└─ 复杂算子 → 走 TileLang 设计表达路径
95+ ├─ Elementwise / 激活函数 / 双输入逐元素:
96+ │ ReLU, Sigmoid, SiLU, GELU, SwiGLU, Add, Sub, Mul, Div 等
97+ ├─ Attention: FlashAttention, SparseAttention, GQA...
98+ ├─ MatMul 变体: matmul+leakyrelu, quant_matmul 等
99+ ├─ Norm 变体: RMSNorm, LayerNorm (多 strategy)
100+ ├─ Sort: Sort, TopK
101+ ├─ Pooling: AvgPool, MaxPool 等
102+ └─ 多输入融合: Concat, multi-tensor fused ops
103+```
104+ 
105+### 工作流程
106+ 
107+```
108+Phase 0: 参数确认 + 算子分类 (解析 npu, op_file, output_dir, 判定简单/复杂路径)
109+Phase 1: 环境准备 + 工程初始化 (复制算子文件 + 初始化 kernel 工程 + 算子注册)
110+Phase 2: Case 精简 (tilelang2ascend-case-simplifier)
111+Phase 3: 设计表达 (分支)
112+ ├─ 简单算子: 架构设计 + 设计串讲 (ops-direct-invoke: DESIGN.md + PLAN.md + WALKTHROUGH.md)
113+ └─ 复杂算子: TileLang 设计 (tilelang2ascend-tilelang-designer)
114+ ├─ Step 0: Attention 模式路由 (仅 Attention/FlashAttention 类)
115+ ├─ Step 1: Block 层级设计 (技术约束检测 + 编程模式决策 + 信息收集 + 性能初检[强制门禁,产出 design/PERF_DESIGN.md])
116+ ├─ Step 2: Tile 层级设计 (编码规范 + GEMM/CV融合 + V核并行化 + 性能反模式终检[强制门禁,追加 PERF_DESIGN.md])
117+ ├─ Step 3: 自检 + 退化检测 + 迭代
118+ └─ Step 4: TileLang 性能迭代 [强制门禁] (tilelang-perf-optimization 本体: 基线采集 → 优化点迭代[种子=PERF_DESIGN待验证清单] → 复验; 达标线 geomean≥0.6x, p_retry≤3, 产出 perf_tuning/; 框架侧不可执行且有对照实验证据时产出 SKIPPED.md 合法跳过)
119+ Skill 依赖: tilelang-op-design + tilelang-op-develop + tilelang-perf-optimization
120+Phase 4: AscendC 生成与验证 (分支)
121+ ├─ 简单算子: 开发实现 + 代码审查 + 修复循环 (ops-direct-invoke: 渐进式开发 + REVIEW.md + 最多3轮修复)
122+ └─ 复杂算子: 转译 (tilelang2ascend-translator + 退化检测 + 迭代)
123+Phase 5: 性能分析 (ops-profiling --quick 模式; 只做测量归档, TileLang 调优已在 Phase 3 Step 4 完成)
124+Phase 6: 全量验证 (恢复全量用例、验证、修复)
125+Phase 7: Trace 记录 + 知识演进 (tilelang2ascend-trace-recorder: trace.md →
126+ 从 trace.md 提取走偏点与成功模式 → 经 ops-knowledge-ingest
127+ 标准路由按 OKF 格式写入共享知识库 runbooks/
128+ ($CANNBOT_KNOWLEDGE_ROOT),供后续任务经
129+ knowledge-query 检索,形成生成效率闭环)
130+```
131+ 
132+### Phase 执行规则
133+ 
134+1. **调用 Subagent**:调用 `ascend-kernel-developer` Subagent,传入用户需求参数
135+2. **进度监控**:定期检查 `{output_dir}/` 下的产出文件,判断当前 Phase
136+3. **完成判定**:Phase 7 产出 `trace.md` 并完成知识演进(runbooks/ 新卡或
137+ 更新 + 逐层 index.md 同步 + 检索索引重建 + log 记录),流程方为完成
138+ (演进失败不阻塞汇报,但需如实说明)
139+4. **中断恢复**:如果流程中断,检查已有文件判断当前进度,从断点继续
140+ 
141+ 
142+### D 类精度修复重入流程
143+ 
144+当 Subagent 完成全流程后返回 Phase 4 为 D 类(精度不匹配)时,Orchestrator **不得自行修改代码**,必须通过以下重入循环调度 Subagent 完成精度修复:
145+ 
146+```
147+Subagent 返回 (Phase 4 = D 类, d_retry = N)
148+ │
149+ ├─ N >= 12 → 暂停,向用户汇报"精度修复已达上限"
150+ │
151+ └─ N < 12 → 重新调用 ascend-kernel-developer Subagent
152+ │
153+ 传入:
154+ - output_dir, op_file, npu(原始参数)
155+ - resume_from: "phase_4_d_class"
156+ - d_retry: N + 1
157+ - 上一轮的 trace.md 摘要(错误信息、已尝试的修复方向)
158+ │
159+ Subagent 内部执行:
160+ - 读取当前 kernel 代码和 evaluate_ascendc.sh 错误
161+ - 按 tilelang2ascendc-kernel-generator.md 4.5D 流程执行一轮:
162+ [D1-1]/[D2-1] → 调用 Skill → 获取修复建议 → 修改代码 → evaluate_ascendc.sh
163+ - 更新 trace.md,记录本轮的 d_retry、修复内容、验证结果
164+ │
165+ Subagent 返回本轮结果
166+ │
167+ ├─ PASS → 进入 Phase 5(性能分析)
168+ ├─ A 类 → 重新调用 Subagent(resume_from: "phase_4_a_class")
169+ ├─ D 类 → 回到本流程开头(d_retry 递增)
170+ └─ B/C 类 → 按对应错误处理
171+```
172+ 
173+**Orchestrator 职责**:
174+1. 检查 Subagent 返回的 `d_retry`,判断是否在预算内
175+2. 每次调用 Subagent 时传入正确的 `d_retry` 值(严格递增,不跳号)
176+3. 禁止在两次 Subagent 调用之间自行修改代码
177+4. 禁止调用 precision-debug/precision-tuning Skill 然后自己修代码(Skill 只能由 Subagent 在 D 类流程内部调用)
178+ 
179+**Subagent prompt 规范**:
180+- 必须明确告知 Subagent 当前处于 D 类修复的第几轮(d_retry=N)
181+- 必须传入上一轮的失败信息和建议(避免重复已证无效的方向)
182+- 禁止在 prompt 中内联 kernel 代码或设计文档内容(违反 S2 约束)
183+ 
184+### Subagent 返回后强制检查点(Orchestrator 必须执行,不可跳过)
185+ 
186+Subagent 完成工作流返回后,Orchestrator **必须**执行以下检查,**不得**在检查通过前向用户汇报"流程完成":
187+ 
188+```
189+Subagent 返回
190+ │
191+ ├─ 步骤 1: 读取 {output_dir}/trace.md
192+ │ 确认 Phase 4 (AscendC 生成与验证) 的最终结果
193+ │
194+ ├─ 步骤 2: 读取 .claude/hooks/state/precision_gate.json
195+ │ 交叉验证 d_class_active 状态
196+ │
197+ ├─ 步骤 3: 判定
198+ │ ├─ Phase 4 = PASS → 流程完成,可向用户汇报
199+ │ │
200+ │ ├─ Phase 4 = D 类(或 precision_gate.json 中 d_class_active=true)
201+ │ │ 且 d_retry < 12:
202+ │ │ → 立即自动重新调用 Subagent(d_retry = N+1)
203+ │ │ → 不等待用户确认,不向用户汇报"完成"
204+ │ │ → 在 prompt 中明确告知: "当前为 D 类精度修复第 N+1 轮,禁止跳过 4.5D 流程"
205+ │ │
206+ │ └─ Phase 4 = D 类 且 d_retry >= 12:
207+ │ → 向用户汇报精度修复已达上限,请求人工介入
208+ │
209+ └─ Phase 4 = A/B/C 类 → 按错误处理表对应处理
210+```
211+ 
212+**关键约束**:
213+- 此检查点是**强制性的**,每次 Subagent 返回后必须执行,不得遗漏
214+- Orchestrator **不得**在看到 D 类结果时向用户汇报"流程完成"或等待用户指令
215+- D 类自动重入是 Orchestrator 的核心职责,不是"可选的用户确认"
216+- 如果 trace.md 中 Subagent 声称 D 类但以"API 限制/框架 bug"为由跳过修复 → 仍视为 D 类,强制重入
217+- 如果 precision_gate.json 中 d_class_active=true 但 trace.md 未明确标注 D 类 → 以 gate 为准,强制重入
218+ 
219+### 错误处理
220+ 
221+| 错误类型 | 处理方式 |
222+|---------|---------|
223+| Subagent 返回失败 | 分析失败原因,判断是代码问题还是环境问题 |
224+| 代码问题 | 允许 Subagent 自动迭代修复(不超过各 Phase 上限) |
225+| 环境问题 | 告知用户,停止流程 |
226+| 超过迭代上限 | 暂停,上报用户 |
227+| 用户中断 | 保留已完成产出,下次可从断点继续 |
228+| Phase 4 D 类 | 通过 D 类精度修复重入流程调度 Subagent,禁止 Orchestrator 直接修复 |
229+ 
230+---
231+ 
232+## Constraint Layer(约束层)
233+ 
234+### Subagent 调用规则
235+ 
236+| # | 规则 |
237+|---|------|
238+| S1 | **禁止**自行编写、删减、改写 Subagent prompt 内容 |
239+| S2 | **禁止**在 Subagent prompt 中内联设计文档内容 |
240+| S3 | **禁止**跳过 Phase 直接开始后续工作 |
241+ 
242+### 高风险行为限制
243+ 
244+- TileLang 退化检测连续 ≥ 3 次 → 自动终止
245+- 简单算子 REVIEW.md 修复循环上限 3 轮 → 超限暂停
246+- TileLang 性能迭代(Phase 3 Step 4)p_retry 上限 3 轮 → 超限必须产出 final_report.md(含上限分析)后继续,不阻塞流程;禁止以"连续无提升"为由空报终止
247+- AscendC Phase 4 迭代: A 类(编译/运行时错误)上限 5 次(a_retry),D 类(精度不匹配)上限 D1 7 次 + D2 5 次 = 12 次(d_retry)。A/D 计数器相互独立,A 类用完后若转入 D 类则 D 类从 0 开始有完整 12 次机会。
248+- 禁止修改 `{output_dir}/` 之外的任何文件
249+- TileLang 功能验证(evaluate_tilelang.sh)默认必须执行且精度必须通过——精度通过是 Phase 3 Step 4 性能迭代的强制前置;仅当对照实验证明属 TileLang 编译器/框架底层不支持时才可跳过(需记录证据)并继续 Phase 4,其余失败必须迭代修复
250+- 禁止手动修改或删除 `.claude/hooks/state/precision_gate.json`(由 Hook 自动管理,绕过即违规)
251+ 
252+---
253+ 
254+## 参考资料
255+ 
256+### 仲裁参考资源
257+ 
258+| 资源类型 | 路径 | 说明 |
259+|---------|------|------|
260+| API 文档 | `asc-devkit/docs/zh/api/` | 仲裁 API 争议时查阅 |
261+| 官方示例 | `asc-devkit/examples/` | 仲裁开发争议时参考 |
262+| 精度调试 Skill | `/tilelang2ascend-precision-tuning` | 仲裁精度争议时参考 |
263+| 性能采集 Skill | `/ops-profiling` | 仲裁性能争议时参考 |
264+| 历史成功任务 | `workflows/templates/archive_tasks/` | 仲裁开发模式争议时参考 |
265+| 演进知识库 | 共享 OKF 知识库 `$CANNBOT_KNOWLEDGE_ROOT`(当前 `/home/asc-gen-knowledge`,配置于 `~/.config/cannbot/knowledge.env`)的 `runbooks/` 树;**检索一律经 `knowledge-query` skill**(preflight/search,禁自维护索引),**沉淀一律经 `ops-knowledge-ingest` skill 路由** | 历史走偏点与成功模式(OKF kind:implementation_trap/operator_optimization,维度走 tags),生成前 preflight 检索可规避已知坑 |
266+ 
267+### TileLang 设计阶段 Skill 依赖(复杂算子路径)
268+ 
269+> 以下 Skill 由 `tilelang2ascend-tilelang-designer` 在设计阶段引用,提供设计方法论、编码规范和性能避坑指导。
270+ 
271+| Skill | 用途 | 使用阶段 |
272+|-------|------|----------|
273+| `ops/tilelang-op-design/SKILL.md` | 算子设计文档生成:技术约束检测、编程模式决策树、信息收集、Block 级设计 | Phase 3 Step 1 |
274+| `ops/tilelang-op-develop/SKILL.md` | 算子代码生成:编码规范、GEMM/CV 融合、V 核并行化、TileLang 编程指南 | Phase 3 Step 2 |
275+| `ops/tilelang-perf-optimization/SKILL.md` | 性能优化:反模式清单、优化指南、CV 同步调试、Cube 高级策略 | Phase 3 Step 1e, 2e(设计门禁)+ Step 4(性能迭代本体) |
@@ -0,0 +1,463 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+"""
12+Documentation Gate Hook — enforces asc-devkit doc reading before kernel code edits.
13+ 
14+Mode: --mode pre (PreToolUse): gates Edit/Write to kernel/**/*.{cpp,h}
15+ --mode post (PostToolUse): tracks Read calls to asc-devkit/docs/ and asc-devkit/examples/
16+ --mode skill_post (PostToolUse): tracks precision Skill calls, grants precision edit budget
17+ 
18+State file: .claude/hooks/state/doc_gate.json
19+ 
20+Protocol: reads tool invocation JSON from stdin, outputs PreToolUse/PostToolUse response JSON to stdout.
21+"""
22+import json
23+import os
24+import sys
25+import logging
26+ 
27+LOGGER = logging.getLogger(__name__)
28+ 
29+ 
30+# ── constants ────────────────────────────────────────────────
31+PROJECT_ROOT = os.environ.get(
32+ "CLAUDE_PLUGIN_ROOT",
33+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
34+)
35+STATE_DIR = os.path.join(PROJECT_ROOT, ".claude", "hooks", "state")
36+STATE_FILE = os.path.join(STATE_DIR, "doc_gate.json")
37+PRECISION_GATE_FILE = os.path.join(STATE_DIR, "precision_gate.json")
38+ 
39+# Budget: how many kernel-file Edit/Write ops are allowed per coverage level.
40+# Proportional to foundational doc category coverage (0-4 categories).
41+BUDGET_BY_COVERAGE = {
42+ 0: 0, # blocked
43+ 1: 10, # minimal
44+ 2: 20, # partial
45+ 3: 35, # good
46+ 4: 50, # full (all 4 categories including example code)
47+}
48+ 
49+# Files under these path segments are considered kernel source code.
50+KERNEL_SEGMENTS = ("/kernel/", "/op_kernel/", "/op_host/")
51+KERNEL_EXTENSIONS = (".cpp", ".h", ".hpp", ".cc")
52+ 
53+# Files under these path segments are considered documentation.
54+DOC_SEGMENTS = ("/asc-devkit/docs/", "/asc-devkit/examples/")
55+ 
56+# ── foundational doc categories ──────────────────────────────
57+# Each category has patterns (path substrings) and a human-readable description.
58+# An agent must read at least ONE doc matching each category to unlock full budget.
59+FOUNDATIONAL_CATEGORIES = {
60+ "矢量编程流水线指南": {
61+ "patterns": [
62+ "/guide/operator_practice/simd_operator_impl/vector_programming/",
63+ ],
64+ "desc": "CopyIn→Compute→CopyOut 正确流水线模式",
65+ "example": "asc-devkit/docs/zh/guide/operator_practice/simd_operator_impl/vector_programming/basic_vector_operator.md",
66+ },
67+ "UB缓冲区/TBuf-TQue管理指南": {
68+ "patterns": [
69+ "/guide/operator_practice/simd_operator_impl/vector_programming/tbuf_usage.md",
70+ "/guide/programming_guide/programming_model/ai_core_simd_programming/tpipe_tque_programming/",
71+ ],
72+ "desc": "UB 临时缓冲区正确分配与 TQue/TBuf 使用模式",
73+ "example": "asc-devkit/docs/zh/guide/operator_practice/simd_operator_impl/vector_programming/tbuf_usage.md",
74+ },
75+ "API参考文档": {
76+ "patterns": [
77+ "/api/SIMD-API/",
78+ "/SIMD-API/tensor_api/",
79+ ],
80+ "desc": "所用 API 的完整签名、dtype 支持矩阵、参数约束",
81+ "example": "asc-devkit/docs/zh/api/SIMD-API/basic_api/memory_vector_compute/data_move/DataCopyPad_GMToUB.md",
82+ },
83+ "官方示例代码": {
84+ "patterns": [
85+ "/asc-devkit/examples/01_simd_cpp_api/",
86+ ],
87+ "desc": "官方 CopyIn→Compute→CopyOut 完整 kernel 实现,验证 TQue 管道的正确用法",
88+ "example": (
89+ "asc-devkit/examples/01_simd_cpp_api/02_features/99_acl_based/"
90+ "00_acl_compilation/parallel_ops_package/add_custom/add_custom_kernel.cpp"
91+ ),
92+ },
93+}
94+ 
95+ 
96+# ── helpers ──────────────────────────────────────────────────
97+def ensure_state_dir() -> None:
98+ os.makedirs(STATE_DIR, exist_ok=True)
99+ 
100+ 
101+def read_state() -> dict:
102+ try:
103+ with open(STATE_FILE) as fh:
104+ state = json.load(fh)
105+ except Exception:
106+ return {"docs_read": [], "edits_remaining": 0}
107+ 
108+ # Migrate old format (single last_doc_read → list docs_read)
109+ if "last_doc_read" in state and "docs_read" not in state:
110+ old = state.pop("last_doc_read")
111+ state["docs_read"] = [old] if old else []
112+ if "docs_read" not in state:
113+ state["docs_read"] = []
114+ if "edits_remaining" not in state:
115+ state["edits_remaining"] = 0
116+ 
117+ return state
118+ 
119+ 
120+def write_state(state: dict) -> None:
121+ ensure_state_dir()
122+ with open(STATE_FILE, "w") as fh:
123+ json.dump(state, fh)
124+ 
125+ 
126+def is_kernel_file(file_path: str) -> bool:
127+ """Check whether file_path is a kernel source file under output_dir/kernel/."""
128+ if not file_path:
129+ return False
130+ if not file_path.endswith(KERNEL_EXTENSIONS):
131+ return False
132+ normalized = os.path.normpath(file_path)
133+ if not normalized.startswith("/"):
134+ normalized = "/" + normalized
135+ return any(seg in normalized for seg in KERNEL_SEGMENTS)
136+ 
137+ 
138+def is_doc_file(file_path: str) -> bool:
139+ """Check whether file_path is an asc-devkit documentation or example."""
140+ if not file_path:
141+ return False
142+ return any(seg in file_path for seg in DOC_SEGMENTS)
143+ 
144+ 
145+def get_covered_categories(docs_read: list) -> set:
146+ """Return the set of foundational category names that have at least one doc read."""
147+ covered = set()
148+ for name, cat in FOUNDATIONAL_CATEGORIES.items():
149+ for doc_path in docs_read:
150+ if any(pattern in doc_path for pattern in cat["patterns"]):
151+ covered.add(name)
152+ break
153+ return covered
154+ 
155+ 
156+def get_missing_categories(docs_read: list) -> dict:
157+ """Return {name: category_dict} for categories that have zero docs read."""
158+ covered = get_covered_categories(docs_read)
159+ return {name: cat for name, cat in FOUNDATIONAL_CATEGORIES.items() if name not in covered}
160+ 
161+ 
162+def emit(response: dict) -> None:
163+ LOGGER.info(json.dumps(response, ensure_ascii=False))
164+ 
165+ 
166+# ── precision gate helpers (unchanged) ───────────────────────
167+def read_precision_state() -> dict:
168+ try:
169+ with open(PRECISION_GATE_FILE) as fh:
170+ return json.load(fh)
171+ except Exception:
172+ return {"d_class_active": False, "edits_allowed": 0}
173+ 
174+ 
175+def write_precision_state(state: dict) -> None:
176+ ensure_state_dir()
177+ with open(PRECISION_GATE_FILE, "w") as fh:
178+ json.dump(state, fh)
179+ 
180+ 
181+def check_precision_gate() -> tuple:
182+ """Check precision gate with D1/D2 stage enforcement.
183+ 
184+ Returns (denied: bool, reason: str | None).
185+ """
186+ state = read_precision_state()
187+ if not state.get("d_class_active", False):
188+ return False, None
189+ 
190+ remaining = state.get("edits_allowed", 0)
191+ if remaining > 0:
192+ state["edits_allowed"] = remaining - 1
193+ write_precision_state(state)
194+ return False, None
195+ 
196+ stage = state.get("stage", "D1")
197+ debug_count = state.get("debug_call_count", 0)
198+ tune_count = state.get("tune_call_count", 0)
199+ total = debug_count + tune_count
200+ 
201+ if total >= 12:
202+ return True, (
203+ "[Precision Gate] TERMINAL: Phase 4 D 类精度修复已耗尽全部 12 次机会\n"
204+ f" D-1 (ascendc-precision-debug): {min(debug_count, 7)}/7 次\n"
205+ f" D-2 (ascendc-precision-tuning): {min(tune_count, 5)}/5 次\n"
206+ "\nPhase 4 失败,请进入 Phase 7 记录 trace。"
207+ )
208+ 
209+ if stage == "D1":
210+ used = debug_count
211+ max_calls = 7
212+ budget = 7
213+ required_skill = "ascendc-precision-debug"
214+ stage_info = f"D-1 阶段: ascendc-precision-debug, 已用 {used}/{max_calls} 次"
215+ if debug_count >= 7:
216+ stage_info += "\n[D-1 已耗尽,下次请调用 ascendc-precision-tuning 进入 D-2 深度审计]"
217+ else:
218+ used = tune_count
219+ max_calls = 5
220+ budget = 5
221+ required_skill = "ascendc-precision-tuning"
222+ stage_info = f"D-2 阶段: ascendc-precision-tuning, 已用 {used}/{max_calls} 次"
223+ 
224+ return True, (
225+ "[Precision Gate] BLOCKED: D 类精度失败禁止直接修改 kernel 代码\n"
226+ "\n"
227+ "evaluate_ascendc.sh 判定当前结果为 D 类(精度不匹配)。\n"
228+ f"{stage_info}\n"
229+ "\n"
230+ f"你必须先调用 {required_skill} Skill 进行结构化根因分析:\n"
231+ " 1. 使用 Skill 工具调用上述 skill\n"
232+ " 2. 等待 Skill 返回诊断结论和修复建议\n"
233+ f" 3. Skill 调用后自动授予 {budget} 次编辑预算用于实施修复\n"
234+ "\n"
235+ f"最近 D 类失败摘要:\n{state.get('failure_summary', '')}"
236+ )
237+ 
238+ 
239+def _deny_doc_gate(coverage, missing, budget):
240+ """Build and emit the doc gate denial message."""
241+ lines = [
242+ "[Doc Gate] BLOCKED: 未完成必备文档查阅就尝试修改 kernel 代码",
243+ "",
244+ f"已覆盖 {coverage}/4 类必备文档。缺少以下类别:",
245+ "",
246+ ]
247+ for i, (name, cat) in enumerate(missing.items(), 1):
248+ lines.append(f" [{i}] {name} — {cat['desc']}")
249+ lines.append(f" 示例: {cat['example']}")
250+ lines.append("")
251+ 
252+ lines.append("请先完成上述文档的查阅,然后再修改 kernel 代码。")
253+ lines.append(f"当前覆盖率: {coverage}/4 → 编辑预算: {budget}")
254+ 
255+ emit({
256+ "hookSpecificOutput": {
257+ "hookEventName": "PreToolUse",
258+ "permissionDecision": "deny",
259+ "permissionDecisionReason": "\n".join(lines),
260+ }
261+ })
262+ 
263+ 
264+def _consume_edit_and_allow(state, coverage, budget, covered):
265+ """Consume one edit from budget and emit allow decision."""
266+ remaining = state.get("edits_remaining", budget)
267+ if coverage == 4 and remaining > budget:
268+ remaining = budget
269+ state["edits_remaining"] = max(remaining - 1, 0)
270+ write_state(state)
271+ 
272+ covered_names = ", ".join(sorted(covered))
273+ emit({
274+ "hookSpecificOutput": {
275+ "hookEventName": "PreToolUse",
276+ "permissionDecision": "allow",
277+ "permissionDecisionReason": (
278+ f"[Doc Gate] 全部 {coverage}/4 类必备文档已覆盖 ({covered_names}), "
279+ f"剩余编辑预算: {state['edits_remaining']}"
280+ ),
281+ }
282+ })
283+ 
284+ 
285+# ── handlers ─────────────────────────────────────────────────
286+def handle_pre(tool_name: str, tool_input: dict) -> None:
287+ """PreToolUse: gate Edit/Write to kernel files.
288+ 
289+ Enforces two independent gates:
290+ 1. Doc Gate — foundational doc categories must be covered
291+ 2. Precision Gate — D-class precision failures require structured debugging
292+ """
293+ if tool_name not in ("Edit", "Write"):
294+ emit({"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}})
295+ return
296+ 
297+ file_path = tool_input.get("file_path", "")
298+ if not is_kernel_file(file_path):
299+ emit({"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}})
300+ return
301+ 
302+ state = read_state()
303+ docs_read = state.get("docs_read", [])
304+ covered = get_covered_categories(docs_read)
305+ missing = get_missing_categories(docs_read)
306+ coverage = len(covered)
307+ budget = BUDGET_BY_COVERAGE.get(coverage, 0)
308+ 
309+ if coverage < 4:
310+ _deny_doc_gate(coverage, missing, budget)
311+ return
312+ 
313+ denied, precision_reason = check_precision_gate()
314+ if denied:
315+ emit({
316+ "hookSpecificOutput": {
317+ "hookEventName": "PreToolUse",
318+ "permissionDecision": "deny",
319+ "permissionDecisionReason": precision_reason,
320+ }
321+ })
322+ return
323+ 
324+ _consume_edit_and_allow(state, coverage, budget, covered)
325+ 
326+ 
327+def handle_post(tool_name: str, tool_input: dict) -> None:
328+ """PostToolUse: track doc reads, grant edit budget proportionally."""
329+ if tool_name != "Read":
330+ emit({"hookSpecificOutput": {"hookEventName": "PostToolUse", "permissionDecision": "allow"}})
331+ return
332+ 
333+ file_path = tool_input.get("file_path", "")
334+ if not is_doc_file(file_path):
335+ emit({"hookSpecificOutput": {"hookEventName": "PostToolUse", "permissionDecision": "allow"}})
336+ return
337+ 
338+ state = read_state()
339+ docs_read = state.get("docs_read", [])
340+ 
341+ if file_path not in docs_read:
342+ docs_read.append(file_path)
343+ state["docs_read"] = docs_read
344+ 
345+ # Grant budget based on current coverage
346+ covered = get_covered_categories(docs_read)
347+ coverage = len(covered)
348+ budget = BUDGET_BY_COVERAGE.get(coverage, 0)
349+ missing = get_missing_categories(docs_read)
350+ 
351+ # Reset budget when coverage level changes (don't accumulate)
352+ old_remaining = state.get("edits_remaining", 0)
353+ if coverage == 4:
354+ state["edits_remaining"] = budget
355+ elif old_remaining < budget:
356+ state["edits_remaining"] = budget
357+ 
358+ write_state(state)
359+ 
360+ if coverage < 4:
361+ missing_lines = "\n".join(
362+ f" - {name}: {cat['example']}"
363+ for name, cat in missing.items()
364+ )
365+ msg = (
366+ f"[Doc Gate] 文档已记录 ({coverage}/4 类已覆盖),编辑预算: {budget}\n"
367+ f"\n还需查阅:\n{missing_lines}"
368+ )
369+ else:
370+ covered_names = ", ".join(sorted(covered))
371+ msg = (
372+ f"[Doc Gate] 全部 {coverage}/4 类必备文档已覆盖 ({covered_names})\n"
373+ f"编辑预算: {budget} 次"
374+ )
375+ 
376+ emit({
377+ "hookSpecificOutput": {
378+ "hookEventName": "PostToolUse",
379+ "permissionDecision": "allow",
380+ "additionalContext": msg,
381+ }
382+ })
383+ 
384+ 
385+def handle_skill_post(tool_name: str, tool_input: dict) -> None:
386+ """PostToolUse for Skill: track precision skill calls and grant edit budget."""
387+ if tool_name != "Skill":
388+ emit({"hookSpecificOutput": {"hookEventName": "PostToolUse", "permissionDecision": "allow"}})
389+ return
390+ 
391+ skill_name = tool_input.get("skill", "")
392+ is_precision = skill_name in ("ascendc-precision-debug", "ascendc-precision-tuning")
393+ if not is_precision:
394+ emit({"hookSpecificOutput": {"hookEventName": "PostToolUse", "permissionDecision": "allow"}})
395+ return
396+ 
397+ state = read_precision_state()
398+ if not state.get("d_class_active", False):
399+ emit({"hookSpecificOutput": {"permissionDecision": "allow"}})
400+ return
401+ 
402+ if skill_name == "ascendc-precision-debug":
403+ state["debug_call_count"] = state.get("debug_call_count", 0) + 1
404+ budget = 7
405+ if state["debug_call_count"] >= 7:
406+ state["stage"] = "D2"
407+ elif skill_name == "ascendc-precision-tuning":
408+ state["tune_call_count"] = state.get("tune_call_count", 0) + 1
409+ budget = 5
410+ state["stage"] = "D2"
411+ 
412+ state["skill_just_called"] = True
413+ state["edits_allowed"] = budget
414+ write_precision_state(state)
415+ 
416+ emit({
417+ "hookSpecificOutput": {
418+ "hookEventName": "PostToolUse",
419+ "permissionDecision": "allow",
420+ "additionalContext": (
421+ f"[Precision Gate] 检测到 {skill_name} Skill 调用完成。\n"
422+ f"已授予 {budget} 次 kernel 编辑预算。请根据 Skill 的修复建议修改代码。"
423+ ),
424+ }
425+ })
426+ 
427+ 
428+# ── main ─────────────────────────────────────────────────────
429+def main() -> None:
430+ args = sys.argv[1:]
431+ mode = "pre"
432+ for a in args:
433+ if a.startswith("--mode="):
434+ mode = a.split("=", 1)[1]
435+ elif a == "--mode" or a == "-m":
436+ idx = args.index(a)
437+ if idx + 1 < len(args):
438+ mode = args[idx + 1]
439+ 
440+ raw = sys.stdin.read()
441+ if not raw or not raw.strip():
442+ emit({"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}})
443+ return
444+ 
445+ try:
446+ data = json.loads(raw)
447+ except json.JSONDecodeError:
448+ emit({"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "allow"}})
449+ return
450+ 
451+ tool_name = data.get("tool_name", "")
452+ tool_input = data.get("tool_input") or {}
453+ 
454+ if mode == "post":
455+ handle_post(tool_name, tool_input)
456+ elif mode == "skill_post":
457+ handle_skill_post(tool_name, tool_input)
458+ else:
459+ handle_pre(tool_name, tool_input)
460+ 
461+ 
462+if __name__ == "__main__":
463+ main()
@@ -0,0 +1,60 @@
1+{
2+ "hooks": {
3+ "SessionStart": [
4+ {
5+ "matcher": "startup|resume|clear|compact",
6+ "hooks": [
7+ {
8+ "type": "command",
9+ "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start-tilelang2ascendc-ops-generator",
10+ "async": false
11+ }
12+ ]
13+ }
14+ ],
15+ "PreToolUse": [
16+ {
17+ "matcher": "Bash",
18+ "hooks": [
19+ {
20+ "type": "command",
21+ "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/skill_script_hook.py\"",
22+ "async": false
23+ }
24+ ]
25+ },
26+ {
27+ "matcher": "Edit|Write",
28+ "hooks": [
29+ {
30+ "type": "command",
31+ "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/doc_gate.py\" --mode pre",
32+ "async": false
33+ }
34+ ]
35+ }
36+ ],
37+ "PostToolUse": [
38+ {
39+ "matcher": "Read",
40+ "hooks": [
41+ {
42+ "type": "command",
43+ "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/doc_gate.py\" --mode post",
44+ "async": false
45+ }
46+ ]
47+ },
48+ {
49+ "matcher": "Skill",
50+ "hooks": [
51+ {
52+ "type": "command",
53+ "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/hooks/doc_gate.py\" --mode skill_post",
54+ "async": false
55+ }
56+ ]
57+ }
58+ ]
59+ }
60+}
@@ -0,0 +1,17 @@
1+#!/bin/bash
2+set -e
3+ 
4+# Copy run-hook.cmd from ops-direct-invoke (same pattern)
5+# This script is called by hooks.json to dispatch hook commands
6+HOOK_NAME="$1"
7+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+ 
9+case "$HOOK_NAME" in
10+ session-start-tilelang2ascendc-ops-generator)
11+ source "$SCRIPT_DIR/session-start-tilelang2ascendc-ops-generator"
12+ ;;
13+ *)
14+ echo "Unknown hook: $HOOK_NAME"
15+ exit 1
16+ ;;
17+esac
@@ -0,0 +1,35 @@
1+# ----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# ----------------------------------------------------------------------------------------------------------
10+ 
11+echo "[tilelang2ascendc-ops-generator] Session started."
12+ 
13+# Precision Gate 中断恢复检查
14+GATE_FILE="${CLAUDE_PLUGIN_ROOT}/.claude/hooks/state/precision_gate.json"
15+if [ -f "$GATE_FILE" ]; then
16+ D_CLASS_ACTIVE=$(python3 -c "import json; d=json.load(open('$GATE_FILE')); print(d.get('d_class_active', False))" 2>/dev/null)
17+ if [ "$D_CLASS_ACTIVE" = "True" ]; then
18+ OUTPUT_DIR=$(python3 -c "import json; d=json.load(open('$GATE_FILE')); print(d.get('output_dir', 'unknown'))" 2>/dev/null)
19+ STAGE=$(python3 -c "import json; d=json.load(open('$GATE_FILE')); print(d.get('stage', 'D1'))" 2>/dev/null)
20+ DEBUG_COUNT=$(python3 -c "import json; d=json.load(open('$GATE_FILE')); print(d.get('debug_call_count', 0))" 2>/dev/null)
21+ TUNE_COUNT=$(python3 -c "import json; d=json.load(open('$GATE_FILE')); print(d.get('tune_call_count', 0))" 2>/dev/null)
22+ TOTAL=$((DEBUG_COUNT + TUNE_COUNT))
23+ echo ""
24+ echo "╔══════════════════════════════════════════════════════════════╗"
25+ echo "║ [Precision Gate] D 类精度失败待处理 ║"
26+ echo "╠══════════════════════════════════════════════════════════════╣"
27+ echo "║ output_dir : $OUTPUT_DIR"
28+ echo "║ stage : $STAGE (debug=$DEBUG_COUNT, tune=$TUNE_COUNT, total=$TOTAL/12)"
29+ echo "║ 状态 : 上一轮 Subagent 返回 D 类精度不匹配 ║"
30+ echo "║ Orchestrator 必须: 立即重新调用 ascend-kernel-developer ║"
31+ echo "║ Subagent 进入 D 类精度修复流程 ║"
32+ echo "╚══════════════════════════════════════════════════════════════╝"
33+ echo ""
34+ fi
35+fi
@@ -0,0 +1,362 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+"""
12+Skill 脚本执行 Hook (PreToolUse 协议)。
13+ 
14+对 Bash 工具调用进行拦截:
15+- 命中 INTERCEPTED_PATTERNS: 直接由本 hook 执行脚本,并以 PreToolUse 协议返回
16+ permissionDecision=deny + additionalContext,把执行结果(exit_code/stdout/stderr)
17+ 注入回 agent 的上下文,阻止 harness 二次执行。
18+- 不命中: 输出 permissionDecision=allow,让 harness 正常执行(不在 hook 中重复执行)。
19+ 
20+输入:从 stdin 读取 harness PreToolUse JSON:
21+ {"tool_name": "Bash", "tool_input": {"command": "..."}, ...}
22+ 
23+输出:单行 PreToolUse JSON 到 stdout。
24+"""
25+ 
26+import json
27+import logging
28+import os
29+import re
30+import subprocess
31+import sys
32+import time
33+from pathlib import Path
34+ 
35+ 
36+_protocol_logger = logging.getLogger("skill_script_hook.protocol")
37+_protocol_logger.propagate = False
38+_protocol_logger.setLevel(logging.INFO)
39+_protocol_handler = logging.StreamHandler(sys.stdout)
40+_protocol_handler.setFormatter(logging.Formatter("%(message)s"))
41+_protocol_logger.addHandler(_protocol_handler)
42+ 
43+ 
44+# 每个条目: (interpreter_regex, script_basename_regex)
45+# 只匹配“某个解释器 + 脚本路径”这种真正的脚本调用,
46+# 不匹配命令字符串里仅出现脚本名的情况(如 cat / grep / echo JSON 等)。
47+_PY_INTERP = r"(?:python|python3|python3\.\d+)"
48+_SH_INTERP = r"(?:bash|sh)"
49+ 
50+INTERCEPTED_PATTERNS = [
51+ (_PY_INTERP, r"validate_tilelang_impl\.py"),
52+ (_PY_INTERP, r"validate_ascendc_impl\.py"),
53+ (_SH_INTERP, r"evaluate_tilelang\.sh"),
54+ (_SH_INTERP, r"evaluate_ascendc\.sh"),
55+ (_PY_INTERP, r"performance\.py"),
56+ (_PY_INTERP, r"build_ascendc\.py"),
57+ (_PY_INTERP, r"verification_ascendc\.py"),
58+ (_PY_INTERP, r"verification_tilelang\.py"),
59+]
60+ 
61+# 命令前缀允许的部分(环境变量赋值、cd ... &&、source ... &&)
62+# 这里只是用于推断"真实的脚本调用",不需要覆盖所有 shell 语法。
63+_LEADING_PREFIX = (
64+ r"^\s*"
65+ r"(?:export\s+)?" # 可选的 export 关键字
66+ r"(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*" # ENV=VAL ...
67+ r"(?:cd\s+\S+\s*&&\s*)?" # cd <dir> &&
68+ r"(?:export\s+)?" # 可选的 export 关键字
69+ r"(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*" # 再次允许 ENV=VAL
70+ r"(?:&&\s+)*" # 支持 && 链式连接
71+)
72+ 
73+# 项目根目录:优先从环境变量获取,fallback 到 __file__ 计算
74+# 避免 hook 被以相对路径调用时 __file__ 解析错误
75+PROJECT_ROOT = os.environ.get("PROJECT_ROOT")
76+if not PROJECT_ROOT:
77+ PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
78+ 
79+ 
80+def should_intercept(command: str) -> bool:
81+ # 仅当命令的"实际执行段"是 <interpreter> <path-to-script> 形式时才拦截。
82+ # 兼容:环境变量前缀、可选的 `cd <dir> &&` 前缀。
83+ for interp, script in INTERCEPTED_PATTERNS:
84+ # 路径里允许出现 / 和非空白字符;脚本名用 \b 边界
85+ pattern = (
86+ _LEADING_PREFIX
87+ + interp
88+ + r"\s+"
89+ + r"(?:\S*/)?"
90+ + script
91+ + r"(?:\s|$)"
92+ )
93+ if re.search(pattern, command):
94+ return True
95+ return False
96+ 
97+ 
98+def emit_pretooluse(permission_decision: str, *, reason: str = "", additional_context: str = ""):
99+ output = {
100+ "hookSpecificOutput": {
101+ "hookEventName": "PreToolUse",
102+ "permissionDecision": permission_decision,
103+ }
104+ }
105+ if reason:
106+ output["hookSpecificOutput"]["permissionDecisionReason"] = reason
107+ if additional_context:
108+ output["hookSpecificOutput"]["additionalContext"] = additional_context
109+ _protocol_logger.info(json.dumps(output, ensure_ascii=False))
110+ 
111+ 
112+def truncate(text: str, limit: int = 8000) -> str:
113+ if not text:
114+ return ""
115+ if len(text) <= limit:
116+ return text
117+ head = text[: limit // 2]
118+ tail = text[-limit // 2:]
119+ return f"{head}\n\n... [truncated {len(text) - limit} chars] ...\n\n{tail}"
120+ 
121+ 
122+def _run_command(command: str, cwd: str):
123+ """Execute a shell command and return (exit_code, stdout, stderr, duration_ms)."""
124+ start_time = time.time()
125+ try:
126+ import shlex
127+ cmd_parts = shlex.split(command)
128+ proc = subprocess.run(
129+ cmd_parts, shell=False, cwd=cwd,
130+ capture_output=True, text=True, timeout=1800,
131+ )
132+ exit_code = proc.returncode
133+ stdout = proc.stdout or ""
134+ stderr = proc.stderr or ""
135+ except subprocess.TimeoutExpired as e:
136+ exit_code = 124
137+ stdout = (e.stdout if e.stdout else "")
138+ stderr = (e.stderr if e.stderr else "")
139+ stderr += "\n[HOOK] 命令执行超时(30 分钟)"
140+ except Exception as e:
141+ exit_code = 1
142+ stdout = ""
143+ stderr = f"[HOOK] 执行异常: {type(e).__name__}: {e}"
144+ 
145+ duration_ms = int((time.time() - start_time) * 1000)
146+ return exit_code, stdout, stderr, duration_ms
147+ 
148+# ── Precision Gate (D-class enforcement) ────────────────────
149+_PRECISION_GATE_STATE = os.path.join(
150+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
151+ ".claude", "hooks", "state", "precision_gate.json",
152+)
153+ 
154+# Patterns for D-class detection in evaluate_ascendc.sh output
155+_A_CLASS_ERROR_PATTERNS = [
156+ r"\berror:\s", # compilation error
157+ r"\bfatal error:\s", # fatal compilation error
158+ r"undefined reference", # linker error
159+ r"Segmentation fault",
160+ r"core dumped",
161+ r"cannot find -l", # library not found
162+ r"No such file or directory",
163+ r"CMake Error",
164+ r"make\[\d+\]: \*\*\*", # make build error
165+]
166+_A_CLASS_PATTERN_RE = re.compile("|".join(_A_CLASS_ERROR_PATTERNS), re.IGNORECASE)
167+ 
168+_D_CLASS_PASS_RE = re.compile(r"^\s*Result:\s*(pass|fail)\s*$", re.MULTILINE | re.IGNORECASE)
169+_D_CLASS_NUMERIC_RE = re.compile(r"max_abs_diff\s*=\s*[\d.e+\-]+|MERE\s*=\s*[\d.e+\-]+|matched_ratio\s*=\s*[\d.]+")
170+ 
171+_EVAL_ASCENDC_SCRIPT_RE = re.compile(r"evaluate_ascendc\.sh")
172+ 
173+ 
174+def _is_evaluate_ascendc(command: str) -> bool:
175+ return bool(_EVAL_ASCENDC_SCRIPT_RE.search(command))
176+ 
177+ 
178+def _extract_output_dir(command: str) -> str:
179+ """Extract output_dir argument from evaluate_ascendc.sh command."""
180+ m = re.search(r"evaluate_ascendc\.sh\s+(\S+)", command)
181+ return m.group(1) if m else ""
182+ 
183+ 
184+def _classify_result(exit_code: int, stdout: str, stderr: str) -> str:
185+ """
186+ Classify evaluate_ascendc.sh result as "D" (precision failure),
187+ "A" (code/environment error), or "PASS".
188+ """
189+ combined = f"{stdout}\n{stderr}"
190+ 
191+ # Check for PASS
192+ pass_matches = _D_CLASS_PASS_RE.findall(combined)
193+ if pass_matches:
194+ last_result = pass_matches[-1].lower()
195+ if last_result == "pass":
196+ return "PASS"
197+ 
198+ # Check for A-class (compilation/runtime error)
199+ if _A_CLASS_PATTERN_RE.search(combined):
200+ return "A"
201+ 
202+ # Check for D-class: has numeric diff output and Result: fail
203+ if _D_CLASS_NUMERIC_RE.search(combined):
204+ return "D"
205+ 
206+ # Non-zero exit without clear A-class or D-class patterns → A
207+ if exit_code != 0:
208+ return "A"
209+ 
210+ return "UNKNOWN"
211+ 
212+ 
213+def _ensure_precision_gate_dir() -> None:
214+ os.makedirs(os.path.dirname(_PRECISION_GATE_STATE), exist_ok=True)
215+ 
216+ 
217+def _read_precision_gate() -> dict:
218+ """Read existing precision gate state to preserve counters across iterations."""
219+ try:
220+ with open(_PRECISION_GATE_STATE) as fh:
221+ return json.load(fh)
222+ except Exception:
223+ return {}
224+ 
225+ 
226+def _update_precision_gate(classification: str, stdout: str, output_dir: str) -> None:
227+ """Write or clear precision gate state based on evaluate_ascendc.sh result.
228+ On D-class: resets edits_allowed/skill_just_called but PRESERVES
229+ stage/debug_call_count/tune_call_count across iterations.
230+ On PASS/A: full reset.
231+ """
232+ if classification == "D":
233+ # Extract failure summary (first few lines of case diffs)
234+ summary_lines = []
235+ for line in stdout.splitlines():
236+ if "max_abs_diff" in line or "Result:" in line:
237+ summary_lines.append(line.strip())
238+ failure_summary = "\n".join(summary_lines[:5])
239+ 
240+ # Preserve stage and counters from previous iteration
241+ existing = _read_precision_gate()
242+ stage = existing.get("stage", "D1")
243+ debug_count = existing.get("debug_call_count", 0)
244+ tune_count = existing.get("tune_call_count", 0)
245+ 
246+ # Auto-advance stage based on accumulated counts
247+ if debug_count >= 7:
248+ stage = "D2"
249+ 
250+ state = {
251+ "d_class_active": True,
252+ "output_dir": output_dir,
253+ "edits_allowed": 0,
254+ "skill_just_called": False,
255+ "stage": stage,
256+ "debug_call_count": debug_count,
257+ "tune_call_count": tune_count,
258+ "failure_summary": failure_summary,
259+ }
260+ else:
261+ # PASS or A-class: full reset
262+ state = {"d_class_active": False, "edits_allowed": 0}
263+ 
264+ _ensure_precision_gate_dir()
265+ with open(_PRECISION_GATE_STATE, "w") as fh:
266+ json.dump(state, fh)
267+ 
268+ 
269+def execute_intercepted(command: str) -> None:
270+ cwd = os.getcwd()
271+ if os.path.isdir(PROJECT_ROOT):
272+ cwd = PROJECT_ROOT
273+ 
274+ exit_code, stdout, stderr, duration_ms = _run_command(command, cwd)
275+ 
276+ # Update precision gate state if this is an evaluate_ascendc.sh call
277+ is_eval_ascendc = _is_evaluate_ascendc(command)
278+ classification = None
279+ if is_eval_ascendc:
280+ classification = _classify_result(exit_code, stdout, stderr)
281+ output_dir = _extract_output_dir(command)
282+ _update_precision_gate(classification, stdout, output_dir)
283+ 
284+ additional_context = (
285+ f"[skill_script_hook intercepted execution]\n"
286+ f"command: {command}\n"
287+ f"cwd: {cwd}\n"
288+ f"exit_code: {exit_code}\n"
289+ f"duration_ms: {duration_ms}\n"
290+ f"--- stdout ---\n{truncate(stdout)}\n"
291+ f"--- stderr ---\n{truncate(stderr)}\n"
292+ )
293+ 
294+ status = "成功" if exit_code == 0 else "失败"
295+ 
296+ class_note = ""
297+ if is_eval_ascendc and classification:
298+ class_labels = {"D": "D类-精度不匹配", "A": "A类-代码/编译错误", "PASS": "通过"}
299+ label = class_labels.get(classification, classification)
300+ class_note = f" | 错误分类: {label}"
301+ if classification == "D":
302+ class_note += (
303+ " | [precision_gate] kernel 编辑已锁定,修改代码前必须先调用 "
304+ "ascendc-precision-debug 或 ascendc-precision-tuning Skill"
305+ )
306+ 
307+ reason = (
308+ f"[Hook 拦截提示] 该命令命中 skill_script_hook 拦截规则,"
309+ f"已由 hook 代为执行({status},exit_code={exit_code})。{class_note}"
310+ f"执行结果见下方 additionalContext,原命令已被替换为 no-op,不会重复执行。"
311+ )
312+ 
313+ output = {
314+ "hookSpecificOutput": {
315+ "hookEventName": "PreToolUse",
316+ "permissionDecision": "allow",
317+ "permissionDecisionReason": reason,
318+ "additionalContext": additional_context,
319+ "updatedInput": {
320+ "command": f"echo '[hook-noop] original command was intercepted and executed by skill_script_hook'"
321+ },
322+ }
323+ }
324+ _protocol_logger.info(json.dumps(output, ensure_ascii=False))
325+ 
326+ 
327+def read_stdin_command():
328+ try:
329+ raw = sys.stdin.read()
330+ except Exception:
331+ return None, None
332+ if not raw or not raw.strip():
333+ return None, None
334+ try:
335+ data = json.loads(raw)
336+ except json.JSONDecodeError:
337+ return None, None
338+ if not isinstance(data, dict):
339+ return None, None
340+ tool_name = data.get("tool_name")
341+ tool_input = data.get("tool_input") or {}
342+ command = tool_input.get("command") if isinstance(tool_input, dict) else None
343+ return tool_name, command
344+ 
345+ 
346+def main():
347+ tool_name, command = read_stdin_command()
348+ 
349+ # 没有命令或不是 Bash —— allow,由 harness 处理
350+ if not command or tool_name != "Bash":
351+ emit_pretooluse("allow")
352+ return
353+ 
354+ if should_intercept(command):
355+ execute_intercepted(command)
356+ else:
357+ # 不拦截 —— 让 harness 正常处理(不在此重复执行)
358+ emit_pretooluse("allow")
359+ 
360+ 
361+if __name__ == "__main__":
362+ main()
@@ -0,0 +1,242 @@
1+# CANNBot Ascend C 算子自动生成快速入门指南
2+ 
3+## 概述
4+ 
5+Ascend C 算子自动生成模式适用于**从 PyTorch Model 自动生成 Ascend C Kernel**场景,支持双路径:简单算子走 ops-direct-invoke 工作流(Architect 设计 → Developer 实现 → Reviewer 审查),复杂算子走 TileLang 设计表达 → AscendC 转译优化,端到端完成算子开发。
6+ 
7+### 工作流
8+ 
9+```
10+Phase 0: 参数确认 + 算子分类 (解析输入,判定简单/复杂路径)
11+Phase 1: 环境准备 + 工程初始化 (复制算子文件 + 初始化 kernel 工程)
12+Phase 2: Case 精简 (tilelang2ascend-case-simplifier)
13+Phase 3: 设计表达 (分支)
14+ ├─ 简单算子: 架构设计 + 设计串讲 (ops-direct-invoke: DESIGN.md + PLAN.md + WALKTHROUGH.md)
15+ └─ 复杂算子: TileLang 设计 (tilelang2ascend-tilelang-designer + 退化检测 + 迭代)
16+Phase 4: AscendC 生成与验证 (分支)
17+ ├─ 简单算子: 开发实现 + 代码审查 + 修复循环 (ops-direct-invoke: 渐进式开发 + REVIEW.md + 最多3轮修复)
18+ └─ 复杂算子: 转译 (tilelang2ascend-translator + 退化检测 + 迭代)
19+Phase 5: 性能分析 (ops-profiling --compare 模式)
20+Phase 6: 全量验证
21+Phase 7: Trace 记录 + 知识演进 (tilelang2ascend-trace-recorder: trace.md →
22+ 经 ops-knowledge-ingest 路由按 OKF 格式写入共享知识库
23+ runbooks/,供后续任务经 knowledge-query 检索复用)
24+```
25+ 
26+### 算子分类路由
27+ 
28+| 路径 | 算子类型 | Skill 链 |
29+|------|---------|---------|
30+| 简单算子 | Index, IndexPut, Gather, Scatter, Nonzero, RepeatInterleave, EmbeddingDenseBackward | ops-direct-invoke(Architect → Developer → Reviewer) |
31+| 复杂算子 | Attention, MatMul 变体, Norm 变体, Sort, TopK, 多输入融合 | tilelang2ascend-tilelang-designer → tilelang2ascend-translator |
32+ 
33+## 一、环境搭建
34+ 
35+### 前置条件
36+ 
37+- 已安装 Python 3.8 或更高版本
38+- 已安装 PyTorch 2.0 或更高版本
39+- 已安装 tilelang-ascend,具体安装方式请查阅 [tilelang-ascend readme](https://github.com/tile-ai/tilelang-ascend/blob/ascendc_pto/README.md#method-3-compile-and-install-from-source)
40+- 已安装 CANN Toolkit(建议 ≥ 9.0.0),具体版本配套关系请查阅 [CANN Release Notes](https://www.hiascend.com/cann/document)
41+- 已配置 NPU 设备(支持 Ascend 910/950 PR 等芯片)
42+- 已安装 OpenCode、Claude Code、TRAE、Cursor、Copilot、CodeArts 等受支持的 AI 编程工具
43+ 
44+### Claude Code(推荐)
45+ 
46+**首选:Plugin Marketplace(一键安装)**
47+ 
48+```bash
49+# 注册 marketplace(首次,GitCode 仓库需完整 URL)
50+/plugin marketplace add https://gitcode.com/cann/cannbot-skills.git
51+ 
52+# 安装插件
53+/plugin install tilelang2ascendc-ops-generator@cannbot
54+```
55+ 
56+**备选:init.sh 脚本**
57+ 
58+```bash
59+git clone https://gitcode.com/cann/cannbot-skills.git
60+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
61+bash init.sh project claude # 项目级
62+bash init.sh global claude # 全局级
63+```
64+ 
65+### 其他工具
66+ 
67+<details>
68+<summary> OpenCode</summary>
69+ 
70+```bash
71+git clone https://gitcode.com/cann/cannbot-skills.git
72+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
73+bash init.sh project opencode # 项目级(默认)
74+bash init.sh global opencode # 全局级
75+```
76+ 
77+</details>
78+ 
79+<details>
80+<summary>TRAE</summary>
81+ 
82+仅支持项目级安装。
83+ 
84+```bash
85+git clone https://gitcode.com/cann/cannbot-skills.git
86+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
87+bash init.sh project trae
88+```
89+ 
90+</details>
91+ 
92+<details>
93+<summary>Cursor</summary>
94+ 
95+```bash
96+git clone https://gitcode.com/cann/cannbot-skills.git
97+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
98+bash init.sh project cursor # 项目级
99+bash init.sh global cursor # 全局级
100+```
101+ 
102+安装后在项目根目录生成 `.cursor/` 目录(项目级)或 `~/.cursor/` 目录(全局级),包含 skills/、agents/ 和 AGENTS.md。
103+ 
104+</details>
105+ 
106+<details>
107+<summary>Copilot</summary>
108+ 
109+```bash
110+git clone https://gitcode.com/cann/cannbot-skills.git
111+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
112+bash init.sh project copilot # 项目级
113+bash init.sh global copilot # 全局级
114+```
115+ 
116+安装后在项目根目录生成 `.github/` 目录(项目级)或 `~/.copilot/` 目录(全局级),包含 skills/、agents/ 和 AGENTS.md。
117+ 
118+</details>
119+ 
120+<details>
121+<summary>CodeArts</summary>
122+ 
123+```bash
124+git clone https://gitcode.com/cann/cannbot-skills.git
125+cd cannbot-skills/plugins-community/tilelang2ascendc-ops-generator
126+bash init.sh project codearts # 项目级
127+bash init.sh global codearts # 全局级
128+```
129+ 
130+安装后在项目根目录生成 `.codeartsdoer/` 目录(项目级)或 `~/.codeartsdoer/` 目录(全局级),包含 skills/、agents/ 和 AGENTS.md。
131+ 
132+</details>
133+ 
134+## 二、快速上手
135+ 
136+### 使用方式
137+ 
138+#### 场景一:单算子生成
139+在交互界面中输入算子开发需求:
140+ 
141+```
142+生成ascendC算子,npu=0,算子描述文件为 /path/to/op/model.py,输出到 /path/to/output/op_name/
143+```
144+ 
145+CANNBot 会自动调度 ascend-kernel-developer 按 7 Phase 流程执行:
146+1. 解析参数、判定算子类型(简单/复杂)
147+2. 准备环境、初始化 kernel 工程
148+3. 精简测试用例(≤ 10 个代表性 case)
149+4. 设计表达(简单算子走 ops-direct-invoke 架构设计 + 设计串讲,复杂算子做 TileLang 设计)
150+5. AscendC 生成与验证(简单算子走开发实现 + 代码审查 + 修复循环,复杂算子走转译 + 迭代验证)
151+6. 性能分析(对比 reference 和 ascendc)
152+7. 全量用例验证 + 生成 trace.md 并完成知识演进(共享知识库 runbooks/ 更新)
153+ 
154+#### 场景二:批量性能测试
155+ 
156+适用于对多个已生成算子进行批量性能对比测试,支持单 NPU 串行或多 NPU 并行执行。
157+ 
158+**操作步骤**:
159+ 
160+1. 确保每个算子目录下已有 `model.py`、`model_new_ascendc.py` 和测试用例文件
161+2. 执行批量测试脚本:
162+ 
163+```bash
164+bash .claude/skills/ops-profiling/scripts/msprof_profile_run.sh --batch \
165+ --base-dir /path/to/output_performance \
166+ --max-jobs 7 \
167+ --device-start 1
168+```
169+ 
170+**参数说明**:
171+- `--base-dir`: 包含多个算子输出子目录的根目录(必填)
172+- `--max-jobs`: 最大并发数(默认 7)
173+- `--device-start`: 起始 NPU 设备 ID(默认 1)
174+- `--warm-up`: 预热次数(默认 3)
175+ 
176+### 产出物示例
177+ 
178+```
179+{output_dir}/
180+├── model.py # 算子描述文件(只读)
181+├── <op_name>.json # 原始测试用例文件(备份保留)
182+├── <op_name>.json.bak # 原始 .json 备份
183+├── docs/
184+│ ├── DESIGN.md # 架构设计文档(简单算子路径)
185+│ ├── PLAN.md # 开发计划(简单算子路径)
186+│ ├── WALKTHROUGH.md # 设计串讲记录(简单算子路径)
187+│ └── REVIEW.md # 代码审查报告(简单算子路径)
188+├── design/
189+│ ├── block_level/ # Block-level 设计(复杂算子路径)
190+│ └── tile_level/ # Tile-level 设计(复杂算子路径)
191+├── kernel/ # AscendC kernel 实现
192+├── model_new_tilelang.py # TileLang 实现(复杂算子路径)
193+├── model_new_ascendc.py # AscendC 实现
194+├── performance.json # 性能数据
195+└── trace.md # 执行记录
196+```
197+ 
198+## 三、可用技能
199+ 
200+| Skill | 用途 | 适用路径 |
201+|-------|------|---------|
202+| `tilelang2ascend-case-simplifier` | 测试用例精简 | 所有 |
203+| ops-direct-invoke (Architect + Developer + Reviewer) | 简单算子架构设计、开发实现、代码审查 | 简单算子 |
204+| `tilelang2ascend-tilelang-designer` | TileLang kernel 设计表达 | 复杂算子 |
205+| `tilelang2ascend-translator` | TileLang→AscendC 转译 | 复杂算子 |
206+| `tilelang2ascend-operator-project-init` | Kernel 工程初始化 | 所有 |
207+| `ops-profiling` | 性能测试分析(--compare 模式) | 所有 |
208+| `tilelang2ascend-trace-recorder` | 执行记录生成 | 所有 |
209+ 
210+## 四、常见问题
211+ 
212+### Q: 如何查看帮助信息?
213+ 
214+```bash
215+bash init.sh --help
216+```
217+ 
218+### Q: 项目级和全局安装如何选择?
219+ 
220+- **项目级**:适合多项目开发,每个项目可以有不同配置
221+- **全局**:适合单一项目,全局生效
222+ 
223+### Q: 简单算子和复杂算子有什么区别?
224+ 
225+简单算子(Index, Gather, Scatter 等)走 ops-direct-invoke 工作流:Architect 架构设计 → Developer 渐进式开发 → Reviewer 代码审查(100分制评分 + 修复循环)。复杂算子(Attention, MatMul, Norm, Sort 等)需要先做 TileLang 设计表达,再转译为 AscendC 代码。
226+ 
227+### Q: TileLang 验证失败了怎么办?
228+ 
229+TileLang 当前主要用于设计表达,不是 correctness gate。若 TileLang 验证失败但设计意图正确,可跳过 TileLang 验证直接进入 AscendC 转译阶段。在 trace.md 中会记录跳过原因。
230+ 
231+### Q: AscendC 验证迭代上限是多少?
232+ 
233+最多 3 轮迭代。超过上限后停止并报告当前状态。
234+ 
235+---
236+ 
237+## 总结
238+ 
239+1. 从 PyTorch Model 出发,支持双路径(简单/复杂)端到端完成算子开发
240+2. 使用 `init.sh` 脚本一键安装(OpenCode 推荐),Claude Code 用户也可用 `/plugin install` 一键安装
241+3. 7 Phase 工作流(含 Phase 7 知识演进闭环),自动算子分类路由,退化检测,迭代修复
242+4. 产出物包含设计文档/设计表达、kernel 代码、性能报告、trace 记录和演进知识库条目
@@ -0,0 +1,161 @@
1+# AscendC Ops Developer 开发指南
2+ 
3+## 概述
4+ 
5+本 Team 实现从 PyTorch Model 到 AscendC Kernel 的端到端自动开发流程。
6+ 
7+## 工作流架构
8+ 
9+```
10+用户输入 (npu, op_file, output_dir)
11+ │
12+ ▼
13+CANNBot Primary Agent (AGENTS.md)
14+ │ 解析参数、调度 Subagent
15+ ▼
16+ascend-kernel-developer Subagent
17+ │
18+ ├── Phase 0: 参数确认 + 算子分类(简单/复杂)
19+ ├── Phase 1: 环境准备
20+ ├── Phase 2: Case 精简 (tilelang2ascend-case-simplifier)
21+ ├── Phase 3: 设计表达(分支)
22+ │ ├─ 简单: ops-direct-invoke 架构设计 + 设计串讲
23+ │ └─ 复杂: TileLang 设计 (tilelang2ascend-tilelang-designer) ← 迭代循环
24+ ├── Phase 4: AscendC 生成与验证(分支)
25+ │ ├─ 简单: ops-direct-invoke 开发实现 + 代码审查 + 修复循环(3轮上限)
26+ │ └─ 复杂: AscendC 转译 (tilelang2ascend-translator) ← 迭代循环(3轮上限)
27+ ├── Phase 5: 性能分析 (ops-profiling --compare 模式)
28+ ├── Phase 6: 全量验证
29+ └── Phase 7: Trace 记录 + 知识演进 (tilelang2ascend-trace-recorder:
30+ trace.md → 经 ops-knowledge-ingest 路由写入共享知识库 runbooks/)
31+```
32+ 
33+## Phase 3-4 迭代机制
34+ 
35+### Phase 3: TileLang 设计迭代
36+ 
37+```
38+generation → AST退化检测 → [通过] → 功能验证 → [通过] → 完成
39+ ↓ 失败 ↓ 失败
40+ Conductor分析 → 生成修复建议 → 重新生成
41+ ↑
42+ 最多 5 轮迭代 ─┘
43+```
44+ 
45+退化子类型:
46+- Type1: 无 TileLang kernel 导入(纯 PyTorch)
47+- Type2: 有导入但 forward() 未调用
48+- Type3: 调用 kernel 但部分计算仍用 PyTorch
49+- Type4: forward() 中存在逐元素 for 循环
50+ 
51+### Phase 4: AscendC 转译迭代
52+ 
53+```
54+generation → AST退化检测 → [通过] → 功能验证 → [通过] → 完成
55+ ↓ 失败 ↓ 失败
56+ Conductor分析 → 生成修复建议 → 重新生成
57+ ↑
58+ 最多 3 轮迭代 ─┘
59+```
60+ 
61+退化子类型:
62+- Type1: 无 AscendC 扩展导入(纯 PyTorch)
63+- Type2: 有导入但 forward() 未调用 kernel
64+- Type3: 调用 kernel 但部分计算仍用 PyTorch
65+- Type4: forward() 中存在逐元素 for 循环
66+ 
67+## 错误分类
68+ 
69+| 分类 | 含义 | 处理 |
70+|------|------|------|
71+| A 类 | 代码逻辑/算法错误(可修复) | 生成修复建议,继续迭代 |
72+| B 类 | 环境/基础设施错误(不可修复) | 立即终止 |
73+| C 类 | 同一 A 类子类型连续 ≥ 3 次 | 立即终止 |
74+ 
75+## 产出物
76+ 
77+```
78+{output_dir}/
79+├── model.py # 算子描述(只读)
80+├── <op_name>.json # 原始测试用例文件(备份保留)
81+├── <op_name>.json.bak # 原始 .json 备份
82+├── design/
83+│ ├── block_level/ # Block-level 设计
84+│ └── tile_level/ # Tile-level 设计
85+├── kernel/ # AscendC kernel
86+├── model_new_tilelang.py # TileLang 实现
87+├── model_new_ascendc.py # AscendC 实现
88+├── preformance.json # 性能数据
89+└── trace.md # 执行记录(Phase 7 知识演进输入)
90+```
91+ 
92+## 知识演进闭环
93+ 
94+- 每个任务完成后,`tilelang2ascend-trace-recorder` 在 Phase 7 从 trace.md 的走偏点/
95+ 行为轮次/关键错误中提取可复用经验,经 `ops-knowledge-ingest` 标准路由按
96+ OKF (okf.v1) 格式写入共享知识库(`$CANNBOT_KNOWLEDGE_ROOT`,当前
97+ `/home/asc-gen-knowledge`)的 `runbooks/` 树(field_notes/optimization 子树;
98+ 维度分类 sync/api/tiling/precision/process 走 tags,不建目录),
99+ 并同步三件套(逐层 index.md + 检索索引/图谱重建 + log)。
100+- 后续任务的 Phase 0 与 Phase 4 转译前经 `knowledge-query` skill 检索知识库
101+ (preflight/search,禁自维护索引),命中"触发条件"即规避已知坑,
102+ 从而降低迭代轮数、提升生成质量。
103+- 知识库卡片可随平台升级失效;失效时以官方文档为准并标记 superseded。
104+ 
105+## 关键约束
106+ 
107+- TileLang 仅用于设计表达,不作为 correctness/performance gate
108+- model_new_*.py 中禁止使用 torch 算子
109+- 必须将核心计算融合成单个算子
110+- 文件操作范围限制在 {output_dir}/ 内
111+- 优先使用块级/向量化操作,避免标量逐元素写法
112+ 
113+## 评测环境清理最佳实践
114+ 
115+### 问题
116+ 
117+`npu-kernelbench` 的 `pre_build()` 使用 `mkdir(exist_ok=True)`,不清理旧工作目录。连续运行时残留三类有害物:
118+ 
119+| 残留物 | 路径 | 影响 |
120+|--------|------|------|
121+| Profile 数据累积 | `profile/<op>-<sol>/<pid>_<timestamp>_ascend_pt/` | 每次运行追加新子目录,profiler 可能读到旧数据,导致延迟测量错误(出现固定异常值) |
122+| CMake 构建缓存 | `build/CMakeCache.txt`, `build/CMakeFiles/` | 可能跳过重新配置,复用旧编译参数 |
123+| traces.jsonl 追加模式 | 默认追加 | 旧 trace 混入新结果 |
124+ 
125+### 方案:运行前清理(推荐)
126+ 
127+评测脚本(`evaluate_ascendc.sh` / `evaluate_tilelang.sh`)执行前,必须清理工作目录和旧 traces:
128+ 
129+```bash
130+# 清理工作目录(CMake 缓存 + 旧 kernel.so + profile 数据)
131+WORK_DIR="/tmp/npu-kernelbench/${OP_NAME}-${SOLUTION_NAME}"
132+rm -rf "$WORK_DIR"
133+ 
134+# 清理旧 traces(避免追加模式混入旧结果)
135+TRACE_FILE="/tmp/npu-kernelbench/traces.jsonl"
136+rm -f "$TRACE_FILE"
137+```
138+ 
139+也可封装为一键脚本:
140+ 
141+```bash
142+#!/bin/bash
143+set -euo pipefail
144+# 1. 清理工作目录
145+rm -rf /tmp/npu-kernelbench/${OP_NAME}-${SOLUTION_NAME}
146+# 2. 清理旧 traces
147+rm -f /tmp/npu-kernelbench/traces.jsonl
148+# 3. source CANN 环境 + 运行评测
149+source <CANN_PATH>/set_env.sh
150+cd <BENCH_DIR>
151+uv run npu-kernelbench eval \
152+ --definition "$DEF" --workload "$WL" --solution "$SOL" \
153+ -o /tmp/npu-kernelbench/traces.jsonl
154+```
155+ 
156+### 验证清理效果
157+ 
158+清理后运行评测,确认:
159+- `traces.jsonl` 的行数 = workload 的 case 数(无多余旧记录)
160+- profile 目录下只有本次运行的子目录
161+- 延迟数据无固定异常值(如多次运行同一 case 延迟完全相同且不合理)
@@ -0,0 +1,48 @@
1+# tilelang2ascendc-ops-generator Task Prompts
2+ 
3+调用 Subagent 前必须读取对应 Phase 的完整 prompt 模板。
4+ 
5+---
6+ 
7+## Phase 0-7: 端到端算子开发
8+ 
9+### 调用方式
10+ 
11+调用 `ascend-kernel-developer` Subagent,传入用户需求参数。
12+ 
13+### Prompt 模板
14+ 
15+```
16+你是 ascend-kernel-developer,负责从 PyTorch Model 出发,端到端完成算子设计表达和 AscendC kernel 落地。支持双路径:简单算子走 ops-direct-invoke 工作流(Architect 设计 → Developer 实现 → Reviewer 审查),复杂算子走 TileLang 设计表达 → AscendC 转译。
17+ 
18+## 任务参数
19+ 
20+- NPU 设备: {npu}
21+- 算子描述文件: {op_file}
22+- 输出目录: {output_dir}
23+ 
24+## 执行要求
25+ 
26+按以下 Phase 顺序执行,每完成一个 Phase 汇报状态:
27+ 
28+Phase 0: 参数确认 — 解析参数,设置 ASCEND_RT_VISIBLE_DEVICES={npu}
29+Phase 1: 环境准备 — 创建 {output_dir}/,复制算子文件
30+Phase 2: Case 精简 — 调用 tilelang2ascend-case-simplifier 精简测试用例
31+Phase 3: 设计表达(分支)
32+ ├─ 简单算子: ops-direct-invoke 架构设计 + 设计串讲(DESIGN.md + PLAN.md + WALKTHROUGH.md)
33+ └─ 复杂算子: TileLang 设计 — 调用 tilelang2ascend-tilelang-designer,迭代验证
34+Phase 4: AscendC 生成与验证(分支)
35+ ├─ 简单算子: ops-direct-invoke 开发实现 + 代码审查 + 修复循环(REVIEW.md + 最多3轮修复)
36+ └─ 复杂算子: AscendC 转译 — 调用 tilelang2ascend-translator,迭代验证(最多 3 轮)
37+Phase 5: 性能分析 — 调用 ops-profiling(--compare 模式)
38+Phase 6: 全量验证 — 恢复全量用例,最终验证
39+Phase 7: Trace 记录 — 调用 tilelang2ascend-trace-recorder 生成 trace.md
40+ 
41+## 约束
42+ 
43+- 禁止修改 {output_dir}/ 之外的任何文件
44+- 简单算子 Phase 4 REVIEW.md 修复循环上限 3 轮
45+- 复杂算子 Phase 4 AscendC 验证最多 3 轮迭代
46+- 退化检测必须前置,通过后再执行功能验证
47+- 语言:思考用中文,代码和路径用英文
48+```
@@ -0,0 +1,69 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+"""Shared kernel setup logic for archive_tasks templates.
12+ 
13+Each task's kernel/setup.py delegates to `kernel_setup(op_name, kernel_dir=__file__)`.
14+"""
15+ 
16+import os
17+import glob
18+import subprocess
19+from pathlib import Path
20+ 
21+import setuptools
22+from setuptools.command.build_ext import build_ext
23+from torch_npu.utils.cpp_extension import NpuExtension
24+ 
25+ 
26+def kernel_setup(op_name, kernel_dir, ext_name=None, description=None):
27+ """Run setuptools.setup() with a standard AscendC kernel build configuration.
28+ 
29+ Args:
30+ op_name: Operator name used as the Python package name.
31+ kernel_dir: Path to the kernel directory (use __file__ from the calling setup.py).
32+ ext_name: NpuExtension module name. Defaults to "{op_name}_ext".
33+ description: Optional one-line description for the package.
34+ """
35+ here = Path(kernel_dir).resolve().parent
36+ build_dir = here / "build"
37+ if ext_name is None:
38+ ext_name = f"{op_name}_ext"
39+ if description is None:
40+ description = f"{op_name} AscendC kernel"
41+ 
42+ class _BuildExt(build_ext):
43+ def run(self):
44+ so_files = glob.glob(str(build_dir / f"{ext_name}*.so"))
45+ if not so_files:
46+ os.makedirs(build_dir, exist_ok=True)
47+ soc_ver = os.environ.get("SOC_VERSION", "Ascend910B2")
48+ ascend = os.environ.get("ASCEND_HOME_PATH", "")
49+ subprocess.check_call(
50+ ["cmake", str(here),
51+ f"-DSOC_VERSION={soc_ver}",
52+ f"-DASCEND_CANN_PACKAGE_PATH={ascend}",
53+ "-DCMAKE_BUILD_TYPE=Release"],
54+ cwd=build_dir)
55+ subprocess.check_call(
56+ ["make", f"-j{os.cpu_count()}"], cwd=build_dir)
57+ 
58+ self.build_lib = str(build_dir)
59+ super().run()
60+ 
61+ setuptools.setup(
62+ name=op_name,
63+ version="0.1.0",
64+ description=description,
65+ ext_modules=[NpuExtension(ext_name, sources=[])],
66+ cmdclass={"build_ext": _BuildExt},
67+ license="BSD 3-Clause",
68+ python_requires=">=3.8",
69+ )
@@ -0,0 +1,170 @@
1+/*
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef MATMUL_KERNEL_COMMON_H
12+#define MATMUL_KERNEL_COMMON_H
13+ 
14+#include "kernel_operator.h"
15+ 
16+// CRTP base for MatmulKernel templates shared between quant_matmul and matmul_leakyrelu.
17+// Derived must provide accessors: base_m(), base_n(), base_k(), base_mk(), base_kn(), base_mn(),
18+// l1_prefetch().
19+template <typename Derived, typename aType, typename bType, typename cType>
20+class MatmulKernelBase {
21+ static_assert(std::is_same<aType, bType>::value, "aType and bType must be the same type");
22+ static constexpr uint32_t C0 = 32 / sizeof(aType);
23+ 
24+public:
25+ __aicore__ inline MatmulKernelBase() {}
26+ __aicore__ inline void ComputeBlock(const AscendC::GlobalTensor<aType> &aBlock,
27+ const AscendC::GlobalTensor<bType> &bBlock,
28+ const AscendC::GlobalTensor<cType> &cBlock);
29+ 
30+protected:
31+ AscendC::TQue<AscendC::TPosition::A1, 1> inQueueA1;
32+ AscendC::TQue<AscendC::TPosition::A2, 1> inQueueA2;
33+ AscendC::TQue<AscendC::TPosition::B1, 1> inQueueB1;
34+ AscendC::TQue<AscendC::TPosition::B2, 1> inQueueB2;
35+ AscendC::TQue<AscendC::TPosition::CO1, 1> outQueueCO1;
36+ 
37+ uint32_t k_;
38+ uint32_t a_dvalue_, b_dvalue_;
39+ 
40+ __aicore__ inline void CopyA(const AscendC::GlobalTensor<aType> &A, uint32_t kLen);
41+ __aicore__ inline void CopyB(const AscendC::GlobalTensor<bType> &B, uint32_t kLen);
42+ __aicore__ inline void SplitA(const AscendC::LocalTensor<aType> &a1Local,
43+ uint32_t offset, uint32_t colC0Stride);
44+ __aicore__ inline void SplitB(const AscendC::LocalTensor<bType> &b1Local,
45+ uint32_t offset, uint32_t colC0Stride);
46+ __aicore__ inline void Compute(const AscendC::LocalTensor<cType> &c1Local, bool cmatrixInitVal);
47+ __aicore__ inline void CopyOut(const AscendC::GlobalTensor<cType> &C);
48+ 
49+private:
50+ __aicore__ inline Derived &self() { return static_cast<Derived &>(*this); }
51+ __aicore__ inline const Derived &self() const { return static_cast<const Derived &>(*this); }
52+};
53+ 
54+// ---------------------------------------------------------------------------
55+// ComputeBlock
56+// ---------------------------------------------------------------------------
57+template <typename Derived, typename aType, typename bType, typename cType>
58+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::ComputeBlock(
59+ const AscendC::GlobalTensor<aType> &aBlock,
60+ const AscendC::GlobalTensor<bType> &bBlock,
61+ const AscendC::GlobalTensor<cType> &cBlock)
62+{
63+ AscendC::LocalTensor<cType> c1Local = outQueueCO1.AllocTensor<cType>();
64+ uint32_t kTiles = k_ / self().base_k();
65+ 
66+ for (uint32_t outer = 0; outer < kTiles; outer += self().l1_prefetch()) {
67+ uint32_t count = (kTiles - outer < self().l1_prefetch())
68+ ? (kTiles - outer) : self().l1_prefetch();
69+ uint32_t kLen = count * self().base_k();
70+ 
71+ CopyA(aBlock[outer * self().base_k()], kLen);
72+ CopyB(bBlock[outer * self().base_k() * b_dvalue_], kLen);
73+ 
74+ AscendC::LocalTensor<aType> a1Local = inQueueA1.DeQue<aType>();
75+ AscendC::LocalTensor<bType> b1Local = inQueueB1.DeQue<bType>();
76+ 
77+ for (uint32_t i = 0; i < count; i++) {
78+ SplitA(a1Local, i * self().base_mk(), self().base_m());
79+ SplitB(b1Local, i * self().base_k() * C0, kLen);
80+ Compute(c1Local, (outer + i == 0));
81+ }
82+ 
83+ inQueueA1.FreeTensor(a1Local);
84+ inQueueB1.FreeTensor(b1Local);
85+ }
86+ 
87+ outQueueCO1.EnQue(c1Local);
88+ CopyOut(cBlock);
89+}
90+ 
91+// ---------------------------------------------------------------------------
92+// CopyA
93+// ---------------------------------------------------------------------------
94+template <typename Derived, typename aType, typename bType, typename cType>
95+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::CopyA(
96+ const AscendC::GlobalTensor<aType> &A, uint32_t kLen)
97+{
98+ auto a1Local = inQueueA1.AllocTensor<aType>();
99+ LoadNdGmToNzL1(a1Local, A, self().base_m(), kLen, a_dvalue_);
100+ inQueueA1.EnQue(a1Local);
101+}
102+ 
103+// ---------------------------------------------------------------------------
104+// CopyB
105+// ---------------------------------------------------------------------------
106+template <typename Derived, typename aType, typename bType, typename cType>
107+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::CopyB(
108+ const AscendC::GlobalTensor<bType> &B, uint32_t kLen)
109+{
110+ AscendC::LocalTensor<bType> b1Local = inQueueB1.AllocTensor<bType>();
111+ LoadNdGmToNzL1(b1Local, B, kLen, self().base_n(), b_dvalue_);
112+ inQueueB1.EnQue(b1Local);
113+}
114+ 
115+// ---------------------------------------------------------------------------
116+// SplitA
117+// ---------------------------------------------------------------------------
118+template <typename Derived, typename aType, typename bType, typename cType>
119+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::SplitA(
120+ const AscendC::LocalTensor<aType> &a1Local, uint32_t offset, uint32_t colC0Stride)
121+{
122+ AscendC::LocalTensor<aType> a2Local = inQueueA2.AllocTensor<aType>();
123+ LoadNzL1ToZzL0A(a2Local, a1Local[offset], self().base_m(), self().base_k(), colC0Stride);
124+ inQueueA2.EnQue(a2Local);
125+}
126+ 
127+// ---------------------------------------------------------------------------
128+// SplitB
129+// ---------------------------------------------------------------------------
130+template <typename Derived, typename aType, typename bType, typename cType>
131+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::SplitB(
132+ const AscendC::LocalTensor<bType> &b1Local, uint32_t offset, uint32_t colC0Stride)
133+{
134+ AscendC::LocalTensor<bType> b2Local = inQueueB2.AllocTensor<bType>();
135+ LoadNzL1ToZnL0B(b2Local, b1Local[offset], self().base_k(), self().base_n(), colC0Stride);
136+ inQueueB2.EnQue(b2Local);
137+}
138+ 
139+// ---------------------------------------------------------------------------
140+// Compute
141+// ---------------------------------------------------------------------------
142+template <typename Derived, typename aType, typename bType, typename cType>
143+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::Compute(
144+ const AscendC::LocalTensor<cType> &c1Local, bool cmatrixInitVal)
145+{
146+ AscendC::LocalTensor<aType> a2Local = inQueueA2.DeQue<aType>();
147+ AscendC::LocalTensor<bType> b2Local = inQueueB2.DeQue<bType>();
148+ AscendC::MmadParams mmadParams;
149+ mmadParams.m = self().base_m();
150+ mmadParams.n = self().base_n();
151+ mmadParams.k = self().base_k();
152+ mmadParams.cmatrixInitVal = cmatrixInitVal;
153+ AscendC::Mmad(c1Local, a2Local, b2Local, mmadParams);
154+ inQueueA2.FreeTensor(a2Local);
155+ inQueueB2.FreeTensor(b2Local);
156+}
157+ 
158+// ---------------------------------------------------------------------------
159+// CopyOut
160+// ---------------------------------------------------------------------------
161+template <typename Derived, typename aType, typename bType, typename cType>
162+__aicore__ inline void MatmulKernelBase<Derived, aType, bType, cType>::CopyOut(
163+ const AscendC::GlobalTensor<cType> &C)
164+{
165+ auto c1Local = outQueueCO1.DeQue<cType>();
166+ FixpipeNzL0cToNdGm(C, c1Local, self().base_m(), self().base_n());
167+ outQueueCO1.FreeTensor(c1Local);
168+}
169+ 
170+#endif // MATMUL_KERNEL_COMMON_H
@@ -0,0 +1,194 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+ 
12+"""Shared types and utilities for avg_pool3_d block-level and tile-level designs."""
13+ 
14+from collections import namedtuple
15+from typing import NamedTuple
16+ 
17+import tilelang
18+ 
19+ 
20+pass_configs = {
21+ tilelang.PassConfigKey.TL_ASCEND_AUTO_SYNC: True,
22+ tilelang.PassConfigKey.TL_ASCEND_MEMORY_PLANNING: True,
23+}
24+ 
25+ 
26+class PoolSpatialWindow(NamedTuple):
27+ """Spatial dimension parameters for a pooling kernel window."""
28+ k_h: int
29+ k_w: int
30+ s_h: int
31+ s_w: int
32+ p_h: int
33+ p_w: int
34+ 
35+ 
36+class KernelImpls(NamedTuple):
37+ """Collection of kernel implementation functions for branching dispatch."""
38+ generic_3d: callable
39+ reduce_d: callable
40+ split_c: callable
41+ split_w: callable
42+ multi_w: callable
43+ 
44+ 
45+class PoolParams(NamedTuple):
46+ """Pool kernel parameters for compute_pool_divisor."""
47+ k_d: int
48+ k_h: int
49+ k_w: int
50+ s_d: int
51+ s_h: int
52+ s_w: int
53+ p_d: int
54+ p_h: int
55+ p_w: int
56+ d_dim: int
57+ h_dim: int
58+ w_dim: int
59+ count_include_pad: int
60+ divisor_override: int
61+ pool_size: int
62+ 
63+ 
64+_BlockArgs = namedtuple("_BlockArgs", [
65+ "n_batch", "c_dim", "d_dim", "h_dim", "w_dim", "out_d", "out_h", "out_w",
66+ "k_d", "k_h", "k_w", "s_d", "s_h", "s_w", "p_d", "p_h", "p_w",
67+ "count_include_pad", "divisor_override",
68+ "block_c", "dtype", "split_mode", "split_w_tile_kw", "multi_w_window_w_num",
69+ "in_spatial", "out_spatial", "m_out", "block_m", "m_num",
70+ "used_core_num_m", "tasks_per_core_m", "vec_num", "sub_block_m",
71+ "pool_size", "hw", "split_block_c", "c_num",
72+ "total_blocks_mc", "used_core_num_mc", "tasks_per_core_mc",
73+ "split_w_step", "multi_w_window",
74+])
75+ 
76+ 
77+def parse_block_args(*args):
78+ """Parse variadic avg_pool3_d args and compute block-level tiling parameters."""
79+ a = [*args, 0, "float32", 0, 0, 1]
80+ (
81+ n_batch, c_dim, d_dim, h_dim, w_dim, out_d, out_h, out_w,
82+ k_d, k_h, k_w, s_d, s_h, s_w, p_d, p_h, p_w,
83+ count_include_pad, divisor_override,
84+ block_c, dtype, split_mode, split_w_tile_kw, multi_w_window_w_num,
85+ ) = a[:24]
86+ in_spatial = d_dim * h_dim * w_dim
87+ out_spatial = out_d * out_h * out_w
88+ m_out = n_batch * out_spatial
89+ 
90+ block_m = 0
91+ for candidate in (64, 32, 16, 8, 4, 2):
92+ if candidate <= m_out and m_out % candidate == 0:
93+ block_m = candidate
94+ break
95+ if block_m == 0:
96+ raise ValueError(
97+ f"Unsupported output spatial size: M_out={m_out} is not divisible by any block_M in [2,4,8,16,32,64]"
98+ )
99+ 
100+ m_num = m_out // block_m
101+ if m_num <= 0:
102+ raise ValueError(f"m_num must be positive, got {m_num}")
103+ 
104+ num_physical_cores = 20
105+ used_core_num_m = min(num_physical_cores, m_num)
106+ tasks_per_core_m = (m_num + used_core_num_m - 1) // used_core_num_m
107+ 
108+ vec_num = 2
109+ sub_block_m = block_m // vec_num
110+ 
111+ pool_size = k_d * k_h * k_w
112+ hw = h_dim * w_dim
113+ 
114+ split_block_c = block_c if block_c > 0 else 1
115+ c_num = c_dim // split_block_c
116+ total_blocks_mc = m_num * c_num
117+ used_core_num_mc = min(num_physical_cores, total_blocks_mc)
118+ tasks_per_core_mc = (total_blocks_mc + used_core_num_mc - 1) // used_core_num_mc
119+ 
120+ split_w_step = split_w_tile_kw if split_w_tile_kw > 0 else k_w
121+ multi_w_window = multi_w_window_w_num if multi_w_window_w_num > 0 else 1
122+ 
123+ return _BlockArgs(
124+ n_batch, c_dim, d_dim, h_dim, w_dim, out_d, out_h, out_w,
125+ k_d, k_h, k_w, s_d, s_h, s_w, p_d, p_h, p_w,
126+ count_include_pad, divisor_override,
127+ block_c, dtype, split_mode, split_w_tile_kw, multi_w_window_w_num,
128+ in_spatial, out_spatial, m_out, block_m, m_num,
129+ used_core_num_m, tasks_per_core_m, vec_num, sub_block_m,
130+ pool_size, hw, split_block_c, c_num,
131+ total_blocks_mc, used_core_num_mc, tasks_per_core_mc,
132+ split_w_step, multi_w_window,
133+ )
134+ 
135+ 
136+def decompose_spatial(out_row, out_spatial, out_h, out_w):
137+ """Decompose a flattened output row index into (n_idx, od, oh, ow)."""
138+ n_idx = out_row // out_spatial
139+ out_rem = out_row - n_idx * out_spatial
140+ od = out_rem // (out_h * out_w)
141+ od_rem = out_rem - od * (out_h * out_w)
142+ oh = od_rem // out_w
143+ ow = od_rem - oh * out_w
144+ return n_idx, od, oh, ow
145+ 
146+ 
147+def flatten_5d(*args):
148+ """Flatten a (batch, d, h, w) 5-D coordinate into a row offset."""
149+ n_idx, in_spatial, id_val, hw, h_val, w_dim, w_val = args
150+ return n_idx * in_spatial + id_val * hw + h_val * w_dim + w_val
151+ 
152+ 
153+def compute_pool_divisor(od, oh, ow, params: PoolParams):
154+ """Compute the reciprocal divisor for a pooling window at (od, oh, ow)."""
155+ if params.divisor_override > 0:
156+ return 1.0 / params.divisor_override
157+ if params.count_include_pad > 0:
158+ return 1.0 / params.pool_size
159+ d_begin = max(0, od * params.s_d - params.p_d)
160+ h_begin = max(0, oh * params.s_h - params.p_h)
161+ w_begin = max(0, ow * params.s_w - params.p_w)
162+ d_end = min(params.d_dim, od * params.s_d - params.p_d + params.k_d)
163+ h_end = min(params.h_dim, oh * params.s_h - params.p_h + params.k_h)
164+ w_end = min(params.w_dim, ow * params.s_w - params.p_w + params.k_w)
165+ valid = max(0, d_end - d_begin) * max(0, h_end - h_begin) * max(0, w_end - w_begin)
166+ return 1.0 / valid if valid > 0 else 0.0
167+ 
168+ 
169+def is_reduce_d_fast_path(window: PoolSpatialWindow):
170+ """Return True if only the depth dimension participates in pooling."""
171+ return (window.k_h == 1 and window.k_w == 1
172+ and window.s_h == 1 and window.s_w == 1
173+ and window.p_h == 0 and window.p_w == 0)
174+ 
175+ 
176+def resolve_kernel_mode(window, impls, **kwargs):
177+ """Resolve which kernel implementation to use based on input parameters."""
178+ split_mode = kwargs.get('split_mode', 0)
179+ split_c_ready = kwargs.get('split_c_ready', False)
180+ multi_w_window = kwargs.get('multi_w_window', 1)
181+ default = kwargs.get('default')
182+ if is_reduce_d_fast_path(window):
183+ return impls.reduce_d
184+ if split_mode == 1:
185+ if split_c_ready:
186+ return impls.split_c
187+ raise ValueError("split_c requires divisible channel tiles")
188+ if split_mode == 2:
189+ return impls.split_w
190+ if split_mode == 3:
191+ return impls.multi_w if multi_w_window > 1 else impls.generic_3d
192+ if split_c_ready:
193+ return impls.split_c
194+ return default
@@ -0,0 +1,176 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+ 
12+"""Block-level TileLang design for avg_pool3_d.
13+ 
14+This file mirrors tile-level mode selection and scheduling, but keeps only
15+coarse-grained block partition and Vector-side pipeline skeleton.
16+Fine-grained compute details are intentionally left as TODO comments.
17+"""
18+ 
19+import sys
20+from pathlib import Path
21+ 
22+import tilelang
23+import tilelang.language as T
24+ 
25+# Import shared types and utilities from the common module.
26+_COMMON_DIR = Path(__file__).resolve().parent.parent
27+if str(_COMMON_DIR) not in sys.path:
28+ sys.path.insert(0, str(_COMMON_DIR))
29+from avg_pool3_d_common import (
30+ pass_configs,
31+ PoolSpatialWindow,
32+ KernelImpls,
33+ decompose_spatial,
34+ is_reduce_d_fast_path,
35+ resolve_kernel_mode,
36+ parse_block_args,
37+)
38+ 
39+ 
40+# NOTE: Each @T.prim_func below (generic_3d, reduce_d, split_c, split_w, multi_w)
41+# shares the same pipeline boilerplate (Kernel setup, row/block decomposition,
42+# spatial-dimension flattening). This is intentional: T.lang prim_funcs compile
43+# their body into separate compute graphs and *cannot* share DSL code via a
44+# Python helper. The common structure is listed in full for clarity of each
45+# variant's inner logic.
46+ 
47+ 
48+@tilelang.jit(out_idx=[1], pass_configs=pass_configs)
49+def avg_pool3_d(*args):
50+ (
51+ n_batch, c_dim, d_dim, h_dim, w_dim, out_d, out_h, out_w,
52+ k_d, k_h, k_w, s_d, s_h, s_w, p_d, p_h, p_w,
53+ count_include_pad, divisor_override,
54+ block_c, dtype, split_mode, split_w_tile_kw, multi_w_window_w_num,
55+ in_spatial, out_spatial, m_out, block_m, m_num,
56+ used_core_num_m, tasks_per_core_m, vec_num, sub_block_m,
57+ pool_size, hw, split_block_c, c_num,
58+ total_blocks_mc, used_core_num_mc, tasks_per_core_mc,
59+ split_w_step, multi_w_window,
60+ ) = parse_block_args(*args)
61+ 
62+ def _process_rows(x_in, y_out, row_base, sub_block_m):
63+ for m in T.serial(sub_block_m):
64+ out_row = row_base + m
65+ n_idx, od, oh, ow = decompose_spatial(out_row, out_spatial, out_h, out_w)
66+ _ = (x_in, y_out, n_idx, od, oh, ow)
67+ 
68+ def _process_split_c_rows(x_in, y_out, row_base, c_base, sub_block_m):
69+ for m in T.serial(sub_block_m):
70+ out_row = row_base + m
71+ n_idx, od, oh, ow = decompose_spatial(out_row, out_spatial, out_h, out_w)
72+ _ = (x_in, y_out, c_base, n_idx, od, oh, ow)
73+ 
74+ def _scope_process_rows(x_in, y_out, bx, vid):
75+ with T.Scope("V"):
76+ if bx < m_num:
77+ _process_rows(x_in, y_out, bx * block_m + vid * sub_block_m, sub_block_m)
78+ 
79+ def _scope_process_split_c_rows(*args):
80+ x_in, y_out, task_id, bx, bc, vid = args
81+ with T.Scope("V"):
82+ if task_id < total_blocks_mc:
83+ _process_split_c_rows(x_in, y_out, bx * block_m + vid * sub_block_m,
84+ bc * split_block_c, sub_block_m)
85+ 
86+ def _scope_process_multi_w_rows(x_in, y_out, bx, vid):
87+ with T.Scope("V"):
88+ if bx < m_num:
89+ _process_multi_w_rows(x_in, y_out, bx * block_m + vid * sub_block_m, sub_block_m)
90+ 
91+ def _process_multi_w_aligned(*args):
92+ x_in, y_out, n_idx, od, oh, ow, m, sub_block_m = args
93+ for local_w in T.serial(multi_w_window):
94+ if local_w < sub_block_m - m and local_w < out_w - ow:
95+ _ = (x_in, y_out, n_idx, od, oh, ow + local_w)
96+ 
97+ def _process_multi_w_prefix(*args):
98+ x_in, y_out, n_idx, od, oh, ow, m, sub_block_m, prefix_len = args
99+ for local_w in T.serial(multi_w_window):
100+ if local_w < prefix_len and local_w < sub_block_m - m and local_w < out_w - ow:
101+ _ = (x_in, y_out, n_idx, od, oh, ow + local_w)
102+ 
103+ def _process_multi_w_rows(x_in, y_out, row_base, sub_block_m):
104+ for m in T.serial(sub_block_m):
105+ out_row = row_base + m
106+ n_idx, od, oh, ow = decompose_spatial(out_row, out_spatial, out_h, out_w)
107+ if multi_w_window > 1 and ow % multi_w_window == 0:
108+ _process_multi_w_aligned(x_in, y_out, n_idx, od, oh, ow, m, sub_block_m)
109+ elif m == 0:
110+ prefix_len = multi_w_window - (ow % multi_w_window)
111+ _process_multi_w_prefix(x_in, y_out, n_idx, od, oh, ow, m, sub_block_m, prefix_len)
112+ 
113+ @T.prim_func
114+ def generic_3d(
115+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
116+ y_out: T.Tensor((m_out, c_dim), dtype),
117+ ):
118+ with T.Kernel(used_core_num_m, is_npu=True) as (cid, vid):
119+ core_idx = cid
120+ for local_idx in T.serial(tasks_per_core_m):
121+ bx = core_idx * tasks_per_core_m + local_idx
122+ _scope_process_rows(x_in, y_out, bx, vid)
123+ 
124+ @T.prim_func
125+ def reduce_d(
126+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
127+ y_out: T.Tensor((m_out, c_dim), dtype),
128+ ):
129+ with T.Kernel(used_core_num_m, is_npu=True) as (cid, vid):
130+ core_idx = cid
131+ for local_idx in T.serial(tasks_per_core_m):
132+ bx = core_idx * tasks_per_core_m + local_idx
133+ _scope_process_rows(x_in, y_out, bx, vid)
134+ 
135+ @T.prim_func
136+ def split_c(
137+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
138+ y_out: T.Tensor((m_out, c_dim), dtype),
139+ ):
140+ with T.Kernel(used_core_num_mc, is_npu=True) as (cid, vid):
141+ core_idx = cid
142+ for local_idx in T.serial(tasks_per_core_mc):
143+ task_id = core_idx * tasks_per_core_mc + local_idx
144+ bc = task_id // m_num
145+ bx = task_id % m_num
146+ _scope_process_split_c_rows(x_in, y_out, task_id, bx, bc, vid)
147+ 
148+ @T.prim_func
149+ def split_w(
150+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
151+ y_out: T.Tensor((m_out, c_dim), dtype),
152+ ):
153+ with T.Kernel(used_core_num_m, is_npu=True) as (cid, vid):
154+ core_idx = cid
155+ for local_idx in T.serial(tasks_per_core_m):
156+ bx = core_idx * tasks_per_core_m + local_idx
157+ _scope_process_rows(x_in, y_out, bx, vid)
158+ 
159+ @T.prim_func
160+ def multi_w(
161+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
162+ y_out: T.Tensor((m_out, c_dim), dtype),
163+ ):
164+ with T.Kernel(used_core_num_m, is_npu=True) as (cid, vid):
165+ core_idx = cid
166+ for local_idx in T.serial(tasks_per_core_m):
167+ bx = core_idx * tasks_per_core_m + local_idx
168+ _scope_process_multi_w_rows(x_in, y_out, bx, vid)
169+ 
170+ split_c_ready = block_c > 0 and c_dim % block_c == 0
171+ window = PoolSpatialWindow(k_h=k_h, k_w=k_w, s_h=s_h, s_w=s_w, p_h=p_h, p_w=p_w)
172+ impls = KernelImpls(generic_3d=generic_3d, reduce_d=reduce_d,
173+ split_c=split_c, split_w=split_w, multi_w=multi_w)
174+ return resolve_kernel_mode(window, impls,
175+ split_mode=split_mode, split_c_ready=split_c_ready,
176+ multi_w_window=multi_w_window)
@@ -0,0 +1,269 @@
1+#!/usr/bin/env python3
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+ 
12+import sys
13+from pathlib import Path
14+ 
15+import tilelang
16+import tilelang.language as T
17+ 
18+# Tile-level path setup differs from block-level.
19+_p = Path(__file__).resolve().parent.parent
20+if str(_p) not in sys.path:
21+ sys.path.insert(0, str(_p))
22+import avg_pool3_d_common as _common
23+pass_configs = _common.pass_configs
24+PoolSpatialWindow = _common.PoolSpatialWindow
25+KernelImpls = _common.KernelImpls
26+decompose_spatial = _common.decompose_spatial
27+flatten_5d = _common.flatten_5d
28+compute_pool_divisor = _common.compute_pool_divisor
29+is_reduce_d_fast_path = _common.is_reduce_d_fast_path
30+resolve_kernel_mode = _common.resolve_kernel_mode
31+parse_block_args = _common.parse_block_args
32+ 
33+# NOTE: Each @T.prim_func below (generic_3d, reduce_d, split_c, split_w, multi_w)
34+# shares the same pipeline boilerplate (Kernel setup, UB allocation, row/block
35+# decomposition, spatial-dimension flattening). This is intentional: T.lang
36+# prim_funcs compile their body into separate compute graphs and *cannot* share
37+# DSL code via a Python helper. The common structure is listed in full for
38+# clarity of each variant's inner logic.
39+ 
40+ 
41+@tilelang.jit(out_idx=[1], pass_configs=pass_configs)
42+def avg_pool3_d(*args):
43+ a = parse_block_args(*args)
44+ (
45+ n_batch, c_dim, d_dim, h_dim, w_dim, out_d, out_h, out_w,
46+ k_d, k_h, k_w, s_d, s_h, s_w, p_d, p_h, p_w,
47+ count_include_pad, divisor_override,
48+ block_c, dtype, split_mode, split_w_tile_kw, multi_w_window_w_num,
49+ in_spatial, out_spatial, m_out, block_m, m_num,
50+ _, _, vec_num, sub_block_m,
51+ pool_size, hw, split_block_c, c_num,
52+ _, _, _,
53+ split_w_step, multi_w_window,
54+ ) = a
55+ 
56+ def _process_scope(*args, mode="generic"):
57+ if mode == "split_c":
58+ x_in, y_out, acc_ub, inp_ub, out_ub, row_base, c_base = args
59+ else:
60+ x_in, y_out, acc_ub, inp_ub, out_ub, row_base = args
61+ c_base = 0
62+ with T.Scope("V"):
63+ for m in T.serial(sub_block_m):
64+ _process_scope_row(
65+ x_in, y_out, acc_ub, inp_ub, out_ub,
66+ row_base + m, c_base, mode,
67+ )
68+ 
69+ def _process_scope_row(*args):
70+ x_in, y_out, acc_ub, inp_ub, out_ub, out_row, c_base, mode = args
71+ n_idx, od, oh, ow = decompose_spatial(out_row, out_spatial, out_h, out_w)
72+ T.tile.fill(acc_ub, T.float32(0.0))
73+ for kd in T.serial(k_d):
74+ if mode == "reduce_d":
75+ _accumulate_reduce_d_slice(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, ow, kd)
76+ elif mode == "split_w":
77+ _accumulate_split_w_slice(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, ow, kd)
78+ else:
79+ _accumulate_depth_slice(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, ow, kd, col=c_base)
80+ if divisor_override > 0:
81+ T.tile.mul(out_ub, acc_ub, T.float32(1.0 / divisor_override))
82+ else:
83+ T.tile.mul(out_ub, acc_ub, T.float32(1.0 / pool_size))
84+ T.copy(out_ub, y_out[out_row, c_base])
85+ 
86+ @T.prim_func
87+ def generic_3d(
88+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
89+ y_out: T.Tensor((m_out, c_dim), dtype),
90+ ):
91+ with T.Kernel(m_num, is_npu=True) as (cid, vid):
92+ bx = cid
93+ row_base = bx * block_m + vid * sub_block_m
94+ acc_ub = T.alloc_ub((1, c_dim), dtype)
95+ inp_ub = T.alloc_ub((1, c_dim), dtype)
96+ out_ub = T.alloc_ub((1, c_dim), dtype)
97+ _process_scope(x_in, y_out, acc_ub, inp_ub, out_ub, row_base, mode="generic")
98+ 
99+ @T.prim_func
100+ def reduce_d(
101+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
102+ y_out: T.Tensor((m_out, c_dim), dtype),
103+ ):
104+ with T.Kernel(m_num, is_npu=True) as (cid, vid):
105+ bx = cid
106+ row_base = bx * block_m + vid * sub_block_m
107+ acc_ub = T.alloc_ub((1, c_dim), dtype)
108+ inp_ub = T.alloc_ub((1, c_dim), dtype)
109+ out_ub = T.alloc_ub((1, c_dim), dtype)
110+ _process_scope(x_in, y_out, acc_ub, inp_ub, out_ub, row_base, mode="reduce_d")
111+ 
112+ @T.prim_func
113+ def split_c(
114+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
115+ y_out: T.Tensor((m_out, c_dim), dtype),
116+ ):
117+ with T.Kernel(m_num * c_num, is_npu=True) as (cid, vid):
118+ bx = cid % m_num
119+ bc = cid // m_num
120+ row_base = bx * block_m + vid * sub_block_m
121+ c_base = bc * split_block_c
122+ acc_ub = T.alloc_ub((1, split_block_c), dtype)
123+ inp_ub = T.alloc_ub((1, split_block_c), dtype)
124+ out_ub = T.alloc_ub((1, split_block_c), dtype)
125+ _process_scope(x_in, y_out, acc_ub, inp_ub, out_ub, row_base, c_base, mode="split_c")
126+ 
127+ @T.prim_func
128+ def split_w(
129+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
130+ y_out: T.Tensor((m_out, c_dim), dtype),
131+ ):
132+ with T.Kernel(m_num, is_npu=True) as (cid, vid):
133+ bx = cid
134+ row_base = bx * block_m + vid * sub_block_m
135+ acc_ub = T.alloc_ub((1, c_dim), dtype)
136+ inp_ub = T.alloc_ub((1, c_dim), dtype)
137+ out_ub = T.alloc_ub((1, c_dim), dtype)
138+ _process_scope(x_in, y_out, acc_ub, inp_ub, out_ub, row_base, mode="split_w")
139+ 
140+ def _accumulate_reduce_d_slice(*args, col=0):
141+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, ow, kd = args
142+ id_val = od * s_d - p_d + kd
143+ if 0 <= id_val < d_dim:
144+ in_row = flatten_5d(n_idx, in_spatial, id_val, hw, oh, w_dim, ow)
145+ T.copy(x_in[in_row, col], inp_ub)
146+ T.tile.add(acc_ub, acc_ub, inp_ub)
147+ 
148+ def _accumulate_width_slice(*args, col=0):
149+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, id_val, ih_val, cur_ow = args
150+ for kw in T.serial(k_w):
151+ iw_val = cur_ow * s_w - p_w + kw
152+ if 0 <= iw_val < w_dim:
153+ in_row = flatten_5d(
154+ n_idx, in_spatial,
155+ id_val, hw, ih_val,
156+ w_dim, iw_val)
157+ T.copy(x_in[in_row, col], inp_ub)
158+ T.tile.add(acc_ub, acc_ub, inp_ub)
159+ 
160+ def _accumulate_depth_slice(*args, col=0):
161+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, cur_ow, kd = args
162+ id_val = od * s_d - p_d + kd
163+ if 0 <= id_val < d_dim:
164+ for kh in T.serial(k_h):
165+ ih_val = oh * s_h - p_h + kh
166+ if 0 <= ih_val < h_dim:
167+ _accumulate_width_slice(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw,
168+ id_val, ih_val, cur_ow, col=col)
169+ 
170+ def _accumulate_split_w_element(*args):
171+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, id_val, ih_val, iw_val = args
172+ if 0 <= iw_val < w_dim:
173+ in_row = flatten_5d(
174+ n_idx, in_spatial,
175+ id_val, hw, ih_val,
176+ w_dim, iw_val)
177+ T.copy(x_in[in_row, 0], inp_ub)
178+ T.tile.add(acc_ub, acc_ub, inp_ub)
179+ 
180+ def _accumulate_split_w_chunk(*args):
181+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, id_val, ih_val, ow = args
182+ for kw_base in T.serial((k_w + split_w_step - 1) // split_w_step):
183+ for kw_local in T.serial(split_w_step):
184+ kw = kw_base * split_w_step + kw_local
185+ if kw < k_w:
186+ iw_val = ow * s_w - p_w + kw
187+ _accumulate_split_w_element(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw,
188+ id_val, ih_val, iw_val)
189+ 
190+ def _accumulate_split_w_slice(*args):
191+ x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, ow, kd = args
192+ id_val = od * s_d - p_d + kd
193+ if 0 <= id_val < d_dim:
194+ for kh in T.serial(k_h):
195+ ih_val = oh * s_h - p_h + kh
196+ if 0 <= ih_val < h_dim:
197+ _accumulate_split_w_chunk(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw,
198+ id_val, ih_val, ow)
199+ 
200+ def _accumulate_window(*args):
201+ x_in, acc_ub, inp_ub, out_ub, y_out, out_row, local_w, n_idx, in_spatial, hw, od, oh, cur_ow = args
202+ T.tile.fill(acc_ub, T.float32(0.0))
203+ for kd in T.serial(k_d):
204+ _accumulate_depth_slice(x_in, acc_ub, inp_ub, n_idx, in_spatial, hw, od, oh, cur_ow, kd)
205+ divisor = compute_pool_divisor(
206+ od, oh, cur_ow, k_d, k_h, k_w, s_d, s_h, s_w, p_d, p_h, p_w,
207+ d_dim, h_dim, w_dim, count_include_pad, divisor_override, pool_size)
208+ if divisor > 0:
209+ T.tile.mul(out_ub, acc_ub, T.float32(divisor))
210+ else:
211+ T.tile.fill(out_ub, T.float32(0.0))
212+ T.copy(out_ub, y_out[out_row + local_w, 0])
213+ 
214+ def _process_multi_w_aligned(*args):
215+ x_in, y_out, acc_ub, inp_ub, out_ub, out_row, n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m = args
216+ for local_w in T.serial(multi_w_window):
217+ if local_w < sub_block_m - m and local_w < out_w - ow:
218+ _accumulate_window(
219+ x_in, acc_ub, inp_ub, out_ub, y_out, out_row, local_w,
220+ n_idx, in_spatial, hw, od, oh, ow + local_w)
221+ 
222+ def _process_multi_w_prefix(*args):
223+ x_in, y_out, acc_ub, inp_ub, out_ub, out_row, n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m, prefix_len = (
224+ args
225+ )
226+ for local_w in T.serial(multi_w_window):
227+ if local_w < prefix_len and local_w < sub_block_m - m and local_w < out_w - ow:
228+ _accumulate_window(
229+ x_in, acc_ub, inp_ub, out_ub, y_out, out_row, local_w,
230+ n_idx, in_spatial, hw, od, oh, ow + local_w)
231+ 
232+ def _process_multi_w_branch(*args):
233+ x_in, y_out, acc_ub, inp_ub, out_ub, out_row, n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m = args
234+ if multi_w_window > 1 and ow % multi_w_window == 0:
235+ _process_multi_w_aligned(x_in, y_out, acc_ub, inp_ub, out_ub, out_row,
236+ n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m)
237+ elif m == 0:
238+ prefix_len = multi_w_window - (ow % multi_w_window)
239+ _process_multi_w_prefix(x_in, y_out, acc_ub, inp_ub, out_ub, out_row,
240+ n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m, prefix_len)
241+ 
242+ def _process_multi_w_row(*args):
243+ x_in, y_out, acc_ub, inp_ub, out_ub, row_base = args
244+ with T.Scope("V"):
245+ for m in T.serial(sub_block_m):
246+ out_row = row_base + m
247+ n_idx, od, oh, ow = decompose_spatial(out_row, out_spatial, out_h, out_w)
248+ _process_multi_w_branch(x_in, y_out, acc_ub, inp_ub, out_ub, out_row,
249+ n_idx, in_spatial, hw, od, oh, ow, m, sub_block_m)
250+ 
251+ @T.prim_func
252+ def multi_w(
253+ x_in: T.Tensor((n_batch * in_spatial, c_dim), dtype),
254+ y_out: T.Tensor((m_out, c_dim), dtype),
255+ ):
256+ with T.Kernel(m_num, is_npu=True) as (cid, vid):
257+ bx = cid
258+ row_base = bx * block_m + vid * sub_block_m
259+ acc_ub = T.alloc_ub((1, c_dim), dtype)
260+ inp_ub = T.alloc_ub((1, c_dim), dtype)
261+ out_ub = T.alloc_ub((1, c_dim), dtype)
262+ _process_multi_w_row(x_in, y_out, acc_ub, inp_ub, out_ub, row_base)
263+ 
264+ split_c_ready = block_c > 0 and c_dim % block_c == 0
265+ window = PoolSpatialWindow(k_h=k_h, k_w=k_w, s_h=s_h, s_w=s_w, p_h=p_h, p_w=p_w)
266+ impls = KernelImpls(generic_3d=generic_3d, reduce_d=reduce_d,
267+ split_c=split_c, split_w=split_w, multi_w=multi_w)
268+ return resolve_kernel_mode(split_mode, split_c_ready, multi_w_window,
269+ window, impls, default=impls.generic_3d)
@@ -0,0 +1,94 @@
1+# set the library output dir to the python dir for wheel package build
2+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/python/ascend_kernel/ascend_kernel/lib)
3+ 
4+# host side files
5+FILE(GLOB OP_SRCS
6+ ${PROJECT_OP_SRC_BASE}/register.cpp
7+ ${PROJECT_OP_SRC_BASE}/ops/helloworld/op_host/helloworld.cpp
8+ ${PROJECT_OP_SRC_BASE}/ops/abs/op_host/abs.cpp
9+ ${PROJECT_OP_SRC_BASE}/ops/cumsum/op_host/cumsum.cpp
10+ ${PROJECT_OP_SRC_BASE}/ops/histc/op_host/histc.cpp
11+ ${PROJECT_OP_SRC_BASE}/ops/sum/op_host/sum.cpp
12+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_host/avg_pool3_d.cpp
13+ )
14+if(BUILD_CATLASS_MODULE)
15+ list(APPEND OP_SRCS
16+ ${PROJECT_OP_SRC_BASE}/ops/catlass/op_host/catlass_matmul_basic.cpp
17+ )
18+endif()
19+ 
20+# set the so name
21+set(OP_PLUGIN_NAME ascend_kernel)
22+ 
23+# kernel side files without workspace
24+ascendc_library(no_workspace_kernel STATIC
25+ ${PROJECT_OP_SRC_BASE}/ops/helloworld/op_kernel/kernel_helloworld.cpp
26+ ${PROJECT_OP_SRC_BASE}/ops/abs/op_kernel/abs.cpp
27+ ${PROJECT_OP_SRC_BASE}/ops/cumsum/op_kernel/cumsum.cpp
28+ ${PROJECT_OP_SRC_BASE}/ops/histc/op_kernel/histc.cpp
29+ ${PROJECT_OP_SRC_BASE}/ops/sum/op_kernel/sum.cpp
30+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel/avg_pool3_d_generic.cpp
31+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel/avg_pool3_d_reduce_d.cpp
32+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel/avg_pool3_d_split_c.cpp
33+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel/avg_pool3_d_split_w.cpp
34+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel/avg_pool3_d_multi_w.cpp
35+ )
36+ 
37+# # kernel side files with workspace
38+# set(WORKSPACE_KERNEL_SRCS
39+# ${PROJECT_OP_SRC_BASE}/ops/mla_preprocess/op_kernel/mla_preprocess_kernel.cpp
40+# )
41+# if(BUILD_CATLASS_MODULE)
42+# list(APPEND WORKSPACE_KERNEL_SRCS
43+# ${PROJECT_OP_SRC_BASE}/ops/catlass/op_kernel/catlass_matmul_basic_kernel.cpp
44+# )
45+# endif()
46+# ascendc_library(workspace_kernel STATIC ${WORKSPACE_KERNEL_SRCS})
47+# if(BUILD_CATLASS_MODULE)
48+# ascendc_include_directories(workspace_kernel PRIVATE
49+# ${CATLASS_DIR}/include
50+# )
51+# endif()
52+ 
53+# ascendc_compile_definitions(workspace_kernel PRIVATE
54+# -DHAVE_WORKSPACE
55+# -DHAVE_TILING
56+# )
57+ 
58+# create shared library libascend_kernel.so
59+add_library(${OP_PLUGIN_NAME} SHARED ${OP_SRCS})
60+ 
61+target_link_libraries(${OP_PLUGIN_NAME} PRIVATE
62+ # workspace_kernel
63+ no_workspace_kernel
64+ torch_npu
65+ ascendcl
66+ tiling_api
67+ nnopbase
68+ opapi
69+ register
70+ platform
71+ ascendalog
72+ dl
73+)
74+ 
75+target_link_directories(${OP_PLUGIN_NAME} PRIVATE
76+ ${TORCH_DIR}/lib
77+ ${TORCH_NPU_DIR}/lib
78+)
79+ 
80+target_include_directories(${OP_PLUGIN_NAME} PRIVATE
81+ ${CMAKE_CURRENT_SOURCE_DIR}/utils
82+ ${PROJECT_OP_SRC_BASE}/utils
83+ ${PROJECT_SOURCE_DIR}/include
84+ ${PROJECT_OP_SRC_BASE}/ops/avg_pool3_d/op_kernel
85+ ${TORCH_DIR}/include
86+ ${TORCH_DIR}/include/torch/csrc/api/include
87+ ${TORCH_NPU_DIR}/include/third_party/acl/inc
88+ ${TORCH_NPU_DIR}/include/third_party/hccl/inc
89+ ${TORCH_NPU_DIR}/include
90+ ${PYTHON_INCLUDE_DIR}
91+ ${ASCEND_INCLUDE_DIR}/external
92+ ${ASCEND_INCLUDE_DIR}/experiment/platform
93+ ${ASCEND_INCLUDE_DIR}/experiment/runtime
94+)
@@ -0,0 +1,134 @@
1+#!/bin/bash
2+# ----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------------------------------------
11+ 
12+ 
13+set -e
14+ 
15+BUILD_KERNELS_MODULE="ON"
16+DEBUG_MODE="OFF"
17+ 
18+while getopts ":a:hd" opt; do
19+ case ${opt} in
20+ a )
21+ BUILD_KERNELS_MODULE="OFF"
22+ case "$OPTARG" in
23+ kernels )
24+ BUILD_KERNELS_MODULE="ON"
25+ ;;
26+ * )
27+ echo "Error: Invalid Value"
28+ echo "Allowed value: kernels"
29+ exit 1
30+ ;;
31+ esac
32+ ;;
33+ d )
34+ DEBUG_MODE="ON"
35+ ;;
36+ h )
37+ echo "Use './build.sh' build all modules."
38+ echo "Use './build.sh -a <target>' to build specific parts of the project."
39+ echo " <target> can be:"
40+ echo " kernels Only build ascend_kernel."
41+ exit 1
42+ ;;
43+ \? )
44+ echo "Error: unknown flag: -$OPTARG" 1>&2
45+ echo "Run './build.sh -h' for more information."
46+ exit 1
47+ ;;
48+ : )
49+ echo "Error: -$OPTARG requires a value" 1>&2
50+ echo "Run './build.sh -h' for more information."
51+ exit 1
52+ ;;
53+ esac
54+done
55+ 
56+shift $((OPTIND -1))
57+ 
58+ 
59+export DEBUG_MODE=$DEBUG_MODE
60+ 
61+SOC_VERSION="${1:-Ascend910_9382}"
62+ 
63+ 
64+### Get Current CANN Toolkit Installation Path
65+_CANN_TOOLKIT_INSTALL_PATH=$(cat /etc/Ascend/ascend_cann_install.info | grep "Toolkit_InstallPath" | awk -F'=' '{print $2}')
66+source ${_CANN_TOOLKIT_INSTALL_PATH}/set_env.sh
67+echo -e "\e[1;32mDetected CANN Toolkit Installation Path: ${_CANN_TOOLKIT_INSTALL_PATH}\e[0m"
68+echo -e "\e[1;33mDouble Checking Environment Variables:\e[0m"
69+echo -e "\e[1;32mASCEND_HOME_PATH: ${ASCEND_HOME_PATH}\e[0m"
70+echo -e "\e[1;32mASCEND_TOOLKIT_HOME: ${ASCEND_TOOLKIT_HOME}\e[0m"
71+ 
72+ 
73+ASCEND_INCLUDE_DIR=${ASCEND_TOOLKIT_HOME}/$(arch)-linux/include
74+CURRENT_DIR=$(pwd)
75+PROJECT_ROOT=$(dirname "$CURRENT_DIR")
76+VERSION="1.0.0"
77+OUTPUT_DIR=$CURRENT_DIR/output
78+mkdir -p $OUTPUT_DIR
79+echo "outpath: ${OUTPUT_DIR}"
80+ 
81+COMPILE_OPTIONS=""
82+ 
83+function build_kernels()
84+{
85+ CMAKE_DIR=""
86+ BUILD_DIR="build"
87+ 
88+ cd "$CMAKE_DIR" || exit
89+ 
90+ rm -rf $BUILD_DIR
91+ mkdir -p $BUILD_DIR
92+ 
93+ cmake $COMPILE_OPTIONS \
94+ -DCMAKE_INSTALL_PREFIX="$OUTPUT_DIR" \
95+ -DASCEND_HOME_PATH=$ASCEND_HOME_PATH \
96+ -DASCEND_INCLUDE_DIR=$ASCEND_INCLUDE_DIR \
97+ -DSOC_VERSION=$SOC_VERSION \
98+ -DBUILD_KERNELS_MODULE=$BUILD_KERNELS_MODULE \
99+ -B "$BUILD_DIR" \
100+ -S .
101+ 
102+ cmake --build "$BUILD_DIR" --target install -j 16
103+ cd -
104+}
105+ 
106+function make_ascend_kernel_package()
107+{
108+ cd python/ascend_kernel || exit
109+ 
110+ rm -rf "$CURRENT_DIR"/python/ascend_kernel/dist
111+ cp -v "${CURRENT_DIR}/config.ini" "${CURRENT_DIR}/python/ascend_kernel/ascend_kernel/"
112+ python3 setup.py clean --all
113+ python3 setup.py bdist_wheel
114+ mv -v "$CURRENT_DIR"/python/ascend_kernel/dist/ascend_kernel*.whl ${OUTPUT_DIR}/
115+ rm -rf "$CURRENT_DIR"/python/ascend_kernel/dist
116+ cd -
117+}
118+ 
119+function main()
120+{
121+ 
122+ build_kernels
123+ if pip3 show wheel;then
124+ echo "wheel has been installed"
125+ else
126+ pip3 install wheel==0.45.1
127+ fi
128+ if [[ "$BUILD_KERNELS_MODULE" == "ON" ]]; then
129+ make_ascend_kernel_package
130+ fi
131+ 
132+}
133+ 
134+main
@@ -0,0 +1,54 @@
1+# avg_pool3_d 算子设计文档
2+ 
3+## 算子概述
4+ 
5+`avg_pool3_d` 是 3D 平均池化算子,支持 4D/5D 输入张量 (NCDHW/NDHWC)。
6+ 
7+## 实现变体
8+ 
9+本算子提供 5 种 AscendC kernel 实现,根据输入特征自动选择:
10+ 
11+| 实现 | 触发条件 | 说明 |
12+|------|---------|------|
13+| `generic` | 默认 | 通用实现,逐点计算池化窗口平均值 |
14+| `reduce_d` | kH=1, kW=1, sH=1, sW=1 | 仅对 D 维度做 reduce,优化一维情况 |
15+| `split_c` | tilelang_mode=split_c | 沿 C 维度切分,利用向量化并行 |
16+| `split_w` | tilelang_mode=split_w | 沿 W 维度切分,优化宽方向大 kernel |
17+| `multi_w` | tilelang_mode=multi_w | 多窗口并行,提升 W 方向吞吐 |
18+ 
19+## 目录结构
20+ 
21+```
22+csrc/ops/avg_pool3_d/
23+├── CMakeLists.txt
24+├── design.md
25+├── op_host/
26+│ └── avg_pool3_d.cpp # Host 侧封装:参数处理、tiling、kernel 分发
27+└── op_kernel/
28+ ├── avg_pool3_d_generic.cpp
29+ ├── avg_pool3_d_generic_kernel.h
30+ ├── avg_pool3_d_reduce_d.cpp
31+ ├── avg_pool3_d_reduce_d_kernel.h
32+ ├── avg_pool3_d_split_c.cpp
33+ ├── avg_pool3_d_split_c_kernel.h
34+ ├── avg_pool3_d_split_w.cpp
35+ ├── avg_pool3_d_split_w_kernel.h
36+ ├── avg_pool3_d_multi_w.cpp
37+ ├── avg_pool3_d_multi_w_kernel.h
38+ ├── avg_pool3_d_tiling.h
39+ └── kernel_common.h
40+```
41+ 
42+## Host 侧逻辑
43+ 
44+1. **输出尺寸计算**:参考 PyTorch `avg_pool3d` 公式,支持 `ceil_mode`
45+2. **数据格式转换**:NCDHW -> NDHWC flat (n*d*h*w, c)
46+3. **Tiling 构建**:填充 `AvgPool3DKernelTiling` 结构,包含所有几何参数和调度参数
47+4. **Kernel 分发**:根据 `kH/kW/sH/sW/pH/pW` 选择最优实现变体
48+5. **结果回写**:NDHWC -> NCDHW,4D 输入去除 channel 维度
49+ 
50+## 注册信息
51+ 
52+- `ops.h`:`at::Tensor avg_pool3d(...)`
53+- `register.cpp`:`torch.ops.npu.avg_pool3d`
54+- `csrc/CMakeLists.txt`:已添加 host/kernel 源文件,移除旧 ACLNN 封装