Torch 代码生成(Torch Codegen)¶
概述¶
torch_codegen 将 PyPTO IR 直接生成为可 exec() 的 Python/PyTorch 脚本,用于调试与数值一致性验证。
与生产代码生成(PTO/Orchestration)不同,torch_codegen 的核心目标是:
- 快速复现 IR 语义
- 在 Python 环境中观察中间行为
- 为 Pass 调试和系统用例提供可执行参考
源码位置: python/pypto/debug/torch_codegen.py
入口 API:
设计目标与边界¶
设计目标¶
- 保持 IR 到 Python 表达式/语句的可读映射
- 尽量覆盖 tensor/tile 常见算子
- 支持 cross-core
tpush/tpop的并发语义模拟 - 对可疑输入提供可定位的错误信息(超时、split 维度不合法等)
非目标¶
- 不追求执行性能
- 不模拟 Ascend 真实硬件时序和内存模型
- 不作为训练/推理生产后端
整体架构¶
生成结果由三部分拼接而成:
- 运行时前导(
_PREAMBLE) 提供 helper 函数、tile/tensor 边界处理、cross-core 运行时和并发调度器。 - Group 元信息注入(
_GROUP_META.update(...)) 仅当输入是Program且存在 Group/AIC/AIV 配对函数时注入。 - 函数体代码
TorchCodegen(IRVisitor)逐个函数、逐条语句发射 Python 代码。
数据流:
Program/Function IR -> (可选 _build_group_meta) -> TorchCodegen 访存发射 -> 拼接成字符串脚本
表达式与语句生成模型¶
表达式层¶
_OP_MAP将op_name映射到OpHandler(args, kwargs) -> str_visit_expr_str()强制嵌套Call走 Python 侧visit_call,避免底层 C++ 访问器在嵌套调用时丢失表达式- 对二元/一元 IR 节点通过
_BINARY_OP_STR与统一 visitor 适配方法生成
语句层¶
AssignStmt:var = exprEvalStmt:发射表达式本体(用于副作用调用)ReturnStmt:支持单返回与多返回 tupleScopeStmt:透明展开ForStmt/WhileStmt:将 SSAiter_args + yield降为可变变量更新IfStmt:分支内通过yield写回return_vars
命名与函数隔离¶
变量命名通过 _unique_name() 做三类规整:
- 非标识符字符替换为
_ - 连续下划线折叠
- 关键字与数字开头规避
visit_function() 会重置 _var_names/_name_counter/_yield_targets。这是为了避免 Program 多函数场景下对象 id 复用导致的名字串扰。
算子映射系统(_OP_MAP)¶
算子映射按类别注册:
- Tensor/Tile 通用逐元素、广播、规约、逻辑和位运算
matmul/matmul_acc及其 tile 变体create/full/cast/slice/read/write/assemble/fillpad- cross-core 管道操作
system.*操作(调试场景下按 no-op 处理)
cross-core 相关映射¶
tile.tpush_to_aiv->_cross_core_rt.push_to_aiv(tile, split)tile.tpush_to_aic->_cross_core_rt.push_to_aic(tile, split)tile.tpop_from_aic->_cross_core_rt.pop_from_aic(split)tile.tpop_from_aiv->_cross_core_rt.pop_from_aiv(split)tile.get_subblock_idx->_get_subblock_idx()
split 参数统一通过 _split_mode_to_int() 归一化为整数:
0: NONE1: UP_DOWN2: LEFT_RIGHT- 非法或不可解析取值:抛出
ValueError(fail fast)
Tile/Tensor 边界与有效区语义¶
_PREAMBLE 中的 helper 负责调试执行时的边界行为:
_tile_load:越界时按请求形状补零,并记录有效区_tile_store:仅回写有效区_tensor_slice:越界时物化到请求形状_fillpad:按zero/min/max填充无效区域_assemble:按偏移将 source 的有效区拼回 target
有效区通过两个动态属性传播:
_pypto_valid_shape_pypto_full_shape
Cross-core 的入队/切分/合并路径同样会保留这两个属性,确保边界 tile
在 tpush/tpop 过程中不丢失有效区语义。
Cross-Core 运行时设计¶
结构¶
_CrossCoreRuntime 基于 threading.Condition 实现阻塞队列语义,维护:
- 常规通道:
- 发往 AIV:
_to_aiv与_to_aiv_split[split][lane] - 发往 AIC:
_to_aic与_to_aic_split[split][lane] - no-split 双分发通道:
- 发往 AIV:
_to_aiv_dual_nosplit[lane] - 发往 AIC:
_to_aic_dual_nosplit[lane]
同时提供:
reset(no_split_dual_aiv_dispatch=False):每次 Group 调度前清空通道,并配置 no-split 双分发模式snapshot():输出当前队列深度,用于超时诊断,包含 no-split 双分发模式与 dual-nosplit 队列深度
push/pop 语义¶
push_to_aiv(tile, split) / pop_from_aic(split)¶
split=0且no_split_dual_aiv_dispatch=False:单队列传输split=0且no_split_dual_aiv_dispatch=True:push_to_aiv会把同一 tile 复制后广播到 lane0/lane1 两路队列pop_from_aic按当前 lane (lane in {0,1}) 从对应队列消费split=1/2:push 时按 split 维度切成 lane0/lane1 两块;pop 按当前 lane 消费
push_to_aic(tile, split) / pop_from_aiv(split)¶
split=0且no_split_dual_aiv_dispatch=False:单队列传输split=0且no_split_dual_aiv_dispatch=True:push_to_aic按当前 lane (lane in {0,1}) 分路入队pop_from_aiv等待两路都就绪后成对消费,并返回 lane0 的 payloadsplit=1/2:push 按当前 lane 入队;pop 端等待两路齐备后 merge 返回
split 维度定义:
split=1(UP_DOWN):按dim=0切分/拼接split=2(LEFT_RIGHT):按dim=1切分/拼接
同步与超时¶
- 单次
pop等待超时:_PIPE_WAIT_TIMEOUT_SEC(默认 10s) - Group 混合核整体等待超时:
_MIXED_KERNEL_TIMEOUT_SEC(默认 30s) - Group 超时/失败时,运行时会发出取消信号并唤醒全部等待线程, 避免阻塞线程污染后续调度
超时异常会包含:
- 操作名、split、lane
- 活跃线程名(Group 超时)
- 当前 pipe 快照(队列深度)
Group 混合核并发调度¶
元信息构建(_build_group_meta)¶
对 Program 扫描 FunctionType.Group 函数,并按命名约定配对:
<group>_aic<group>_aiv
元信息字段:
aic/aiv:对应函数名split:优先取 Group 的 split;若为 0,则回退 AIV 的 splitdual_aiv_dispatch:从 AIV attrs 读取;当split==0时,该标记会强制 debug 运行时按双 AIV lane 调度
调度入口¶
visit_call() 遇到 GlobalVar 调用时:
- 若命中
_group_meta,改写为_run_group_call(group_name, *args) - 否则保持普通函数调用
_run_group_call() 在有 meta 时走 _run_mixed_kernels()。
线程模型¶
_run_mixed_kernels(group_name, meta, *args) 行为:
- 固定 1 个 AIC 线程
- AIV 线程数:
split in (1,2)-> 2 lanesplit == 0 and dual_aiv_dispatch == True-> 2 lanesplit == 0 and dual_aiv_dispatch == False-> 1 lane- 每线程写入 thread-local
subblock_idx(AIC 设为 0,AIV 为 lane id) - 运行时模式切换:
- 调度器调用
reset(no_split_dual_aiv_dispatch=(split == 0 and dual_aiv_dispatch)) - 仅在 group meta 指定时启用 no-split 双 lane pipe 语义
- 返回值约定:仅采集
aiv lane0的返回作为 Group 返回值; 若其他 lane/role 产生非None返回,会抛出返回契约错误
形状/类型检查(check_shapes)¶
开启 check_shapes=True 时:
- 对函数参数:只检查 dtype,不检查 shape
- 原因:InCore 参数在边界 tile 场景可能是部分数据
- 对赋值目标:检查 tensor 类型与 dtype;shape 按静态/动态维分别检查
动态维策略:
- 若并非全静态
ConstInt,则检查ndim+ 静态维位置值
错误处理与可观测性¶
主要错误类型:
TypeError:入口 node 类型错误ValueError:不支持的 op、非法 split、lane 不合法、split 维度不可二分RuntimeError:pipe 等待超时、混合核线程失败/超时
并发失败时保留首个线程 traceback,便于快速定位具体函数和 lane。
当前限制¶
system.*操作按 no-op 处理,仅保留语义占位- Group 配对依赖命名约定
<group>_aic/_aiv split=1/2依赖对应切分维度可被 2 整除- Group 返回值固定取 AIV lane0
- 该运行时为单机 Python 语义模拟,不等价于硬件流水线时序
测试建议与覆盖点¶
建议至少覆盖以下维度:
- 三类 split:
NONE/UP_DOWN/LEFT_RIGHT - 三类方向:
V->C、C->V、双向V<->C tile.get_subblock_idxlane 语义split=0 + dual_aiv_dispatch下的双 lane 行为(lane0+lane1)- Group 调度路径是否改写为
_run_group_call - 超时和异常路径(必要时通过构造不配对 push/pop)
可参考:
tests/ut/debug/test_torch_codegen.pytests/st/codegen/torch/test_torch_codegen_cross_core.pytests/st/codegen/torch/test_torch_codegen_qwen3_decode_scope3_mixed.py
与其它代码生成文档的关系¶
- 本文档:Python 调试代码生成(
torch_codegen) - 00-pto_codegen.md:PTO 内核代码生成
- 01-orchestration_codegen.md:编排侧 C++ 代码生成
三者职责互补:torch_codegen 负责“可执行语义对照”,PTO/Orchestration 负责“生产链路生成”。