PyPTO IR 节点层次结构¶
本文档提供所有 IR 节点类型的完整参考,按类别组织。
BNF 语法¶
<program> ::= [ identifier ":" ] { <function> }
<function> ::= "def" identifier "(" [ <param_list> ] ")" [ "->" <type_list> ] ":" <stmt>
<param_list> ::= <param> { "," <param> }
<param> ::= <var> | "(" <var> "," <param_direction> ")"
<param_direction> ::= "In" | "Out" | "InOut"
<type_list> ::= <type> { "," <type> }
<stmt> ::= <assign_stmt> | <if_stmt> | <for_stmt> | <while_stmt> | <return_stmt> | <yield_stmt>
| <eval_stmt> | <seq_stmts> | <scope_stmt>
| <break_stmt> | <continue_stmt>
<assign_stmt> ::= <var> "=" <expr>
<if_stmt> ::= "if" <expr> ":" <stmt_list> [ "else" ":" <stmt_list> ] [ "return" <var_list> ]
<for_stmt> ::= "for" <var> [ "," "(" <iter_arg_list> ")" ] "in"
( "range" | "pl.range" ) "(" <expr> "," <expr> "," <expr>
[ "," "init_values" "=" "(" <expr_list> ")" ] ")" ":" <stmt_list>
[ <return_assignments> ]
<while_stmt> ::= "while" <expr> ":" <stmt_list>
| "for" "(" <iter_arg_list> ")" "in" "pl.while_"
"(" "init_values" "=" "(" <expr_list> ")" ")" ":"
"pl.cond" "(" <expr> ")" <stmt_list>
[ <return_assignments> ]
<yield_stmt> ::= "yield" [ <var_list> ]
<return_stmt> ::= "return" [ <var_list> ]
<eval_stmt> ::= <expr>
<seq_stmts> ::= <stmt> { ";" <stmt> }
<scope_stmt> ::= "with" "pl.at" "(" "level" "=" "pl.Level.CORE_GROUP" ")" ":" <stmt_list>
<break_stmt> ::= "break"
<continue_stmt> ::= "continue"
<expr> ::= <var> | <const_int> | <const_bool> | <const_float> | <call>
| <binary_op> | <unary_op> | <tuple_get_item>
<call> ::= <op> "(" [ <expr_list> ] ")"
<op> ::= identifier | <global_var>
<type> ::= <scalar_type> | <tensor_type> | <tile_type>
| <tuple_type> | <pipe_type> | <unknown_type>
<scalar_type> ::= "ScalarType" "(" <data_type> ")"
<tensor_type> ::= "TensorType" "(" <data_type> "," <shape> [ "," <memref> ] ")"
<tile_type> ::= "TileType" "(" <data_type> "," <shape>
[ "," <tile_type_arg> { "," <tile_type_arg> } ]
")"
<tile_type_arg> ::= <memref> | <tile_view> | <memory_space>
<tuple_type> ::= "TupleType" "(" "[" <type_list> "]" ")"
<pipe_type> ::= "PipeType" "(" <pipe_kind> ")"
<shape> ::= "[" <expr_list> "]"
<data_type> ::= "INT32" | "INT64" | "FP16" | "FP32" | "FP64" | "BOOL" | ...
<memory_space> ::= "DDR" | "Vec" | "Mat" | "Left" | "Right" | "Acc" | "Bias"
<pipe_kind> ::= "S" | "V" | "M" | "MTE1" | "MTE2" | "MTE3" | "ALL" | ...
对于 TileType,每个可选参数最多只能出现一次。如果存在 MemRef,
则必须在 TileType 上同时显式提供 memory_space。
表达式节点¶
| 节点类型 | 字段 | 说明 |
|---|---|---|
| Var | name_hint_, type_ |
变量引用(以指针为标识,而非名称) |
| IterArg | name_hint_, type_, initValue_ |
循环迭代参数(扩展自 Var) |
| ConstInt | value_, dtype_ |
整数常量 |
| ConstBool | value_ |
布尔常量(始终为 BOOL dtype) |
| ConstFloat | value_, dtype_ |
浮点常量 |
| Call | op_, args_, kwargs_, attrs_ |
函数/运算符调用(参见 Call attrs 与 kwargs 的区别) |
| Submit | op_, args_, deps_, core_num_, sync_start_, allow_early_resolve_, kwargs_, attrs_ |
任务启动(pl.submit(...) / pl.spmd_submit(...))。core_num_/sync_start_ 携带 SPMD 启动规格;allow_early_resolve_ 是推测式提前派发(speculative early-dispatch)的开关(下沉为 Arg::set_allow_early_resolve(true))。参见 Submit 与 Call 的区别。 |
| TupleGetItemExpr | tuple_, index_ |
元组元素访问 |
Var 的标识(Identity)¶
变量的标识由对象指针(或等价的 unique_id_)决定,而非 name_hint_。两个具有相同 name_hint_ 的 Var 对象,如果是不同对象,则是不同的变量。该字段命名为 name_hint_(而非 name_),正是为了明确这一语义。
| 字段 | 用途 |
|---|---|
name_hint_ |
用于打印和调试的装饰性标签。属于 IgnoreField —— 不参与结构比较和哈希。 |
unique_id_ |
构造时分配的单调递增 ID,用于确定性哈希。 |
| 对象指针 | 权威标识 —— 两个引用指向同一变量,当且仅当它们指向同一个 Var 对象。 |
// 相同的 name_hint,但是不同的变量
auto x1 = std::make_shared<Var>("x", type, span);
auto x2 = std::make_shared<Var>("x", type, span);
// x1 != x2 —— 尽管共享名称 "x",它们是不同的变量
// 同一个变量的两次引用
auto x_ref = x1;
// x1 == x_ref —— 相同的指针,相同的变量
二元表达式节点¶
| 类别 | 节点 |
|---|---|
| 算术运算 | Add, Sub, Mul, FloorDiv, FloorMod, FloatDiv |
| 数学运算 | Min, Max, Pow |
| 比较运算 | Eq, Ne, Lt, Le, Gt, Ge |
| 逻辑运算 | And, Or, Xor |
| 位运算 | BitAnd, BitOr, BitXor, BitShiftLeft, BitShiftRight |
所有二元表达式包含:lhs_、rhs_、dtype_
一元表达式节点¶
| 节点 | 运算 |
|---|---|
| Abs | 绝对值 |
| Neg | 取反 |
| Not | 逻辑非 |
| BitNot | 按位取反 |
| Cast | 类型转换 |
所有一元表达式包含:operand_、dtype_
Op 和 GlobalVar¶
| 节点类型 | 用途 | 使用场景 |
|---|---|---|
| Op | 通用操作/函数引用 | 外部运算符、内置函数 |
| GlobalVar | 程序内的函数引用 | 程序内函数调用 |
op = ir.Op("my_function"); call = ir.Call(op, [x, y], span) # External
gvar = ir.GlobalVar("helper"); call = ir.Call(gvar, [x], span) # Internal
Call attrs 与 kwargs 的区别¶
Call 同时持有两个有序的字符串键映射,C++ 类型完全一致
(std::vector<std::pair<std::string, std::any>>),但所有权与语义完全不同:
| 字段 | 用途 | 来源 | 保留键 |
|---|---|---|---|
kwargs_ |
用户在调用点书写的语言层关键字参数(例如 kernel(x, y, axis=2)),在 parser、printer 与 Python bindings 之间作为面向用户的数据原样往返。 |
前端 / DSL 解析 | 无 —— 键由用户代码决定。 |
attrs_ |
编译器内部的节点元数据,由 pass / verifier 生产与消费,不会作为 DSL 关键字参数暴露给用户。 | 编译器 pass(以及反序列化器在加载旧 payload 时使用) | "arg_directions"(见下文)。后续新增的内部属性建议使用 "hint.*"、"profile.*" 这类带前缀的命名空间。 |
arg_directions 的存放位置。 解析后的逐参数 ArgDirection 序列以
std::vector<ArgDirection> 形式存储在保留键 attrs_["arg_directions"] 下;
访问器 Call::HasArgDirections()、Call::GetArgDirections()(以及 Python
端的 Call.arg_directions 属性)都是该 attr 的薄封装,
WithArgDirectionsAttr(...) 是构造带该 attr 的 attrs 向量的标准入口。
IRProperty::CallDirectionsResolved 校验的就是该 attr 在 DeriveCallDirections
pass 之后是否存在。
Submit 与 Call 的区别¶
Submit 是与 Call 并列的一等 IR 类型(first-class IR kind),表示在
pl.manual_scope 体内由 pl.submit(...) 发起的任务启动(task launch)。两者
在语义上截然不同,pass 作者必须同时考虑——分派规则参见
.claude/rules/pass-submit-awareness.md。
| 方面 | Call |
Submit |
|---|---|---|
| 语义 | 同步函数调用 | 异步任务启动 |
| 出现位置 | 任意位置 | manual_scope 体内(由 parser 产生),以及作为 pl.at(..., deps=[...]) 作用域外提后的派发点(缺失 as tid 绑定时会得到一个合成的未使用 TaskId Var);在整个流水线中保持不变 |
| 返回类型 | 被调方声明的返回 | Tuple[<callee return>..., Scalar[TASK_ID]] |
是否有 deps |
无 —— 普通 Call 从不携带依赖边(attrs["manual_dep_edges"] 仅出现在由 pl.at 产生的 ScopeStmt 上,在作用域外提时被消费;由 ManualDepsOnSubmitOnly 校验) |
一等的 deps_ 字段 —— Scalar[TASK_ID] Var / Array[N, TASK_ID] Var |
| SPMD 启动规格 | 无 | core_num_(optional<ExprPtr> 块数)+ sync_start_(bool),仅由 pl.spmd_submit 设置;sync_start_ 仅在 core_num_ 存在时才有意义(构造函数强制 sync_start ⇒ core_num);nullopt ⇒ 普通单块 submit |
| Use-def 链 | 仅 args_ |
args_、deps_,以及 core_num_ |
| Python 语法 | out = self.foo(...) |
out, tid = pl.submit(self.foo, ...)(或 pl.spmd_submit(self.foo, ..., core_num=N)) |
parser 发出 Submit;printer / structural-equal / structural-hash / visitor /
mutator(Python 钩子 visit_submit)/ DCE / SSA 全部直接按 Submit 类型分派,
且 Submit 会在整个流水线中存活——没有任何 pass 会把它下沉为普通 Call。
形如 Call 的消费者(DeriveCallDirections、ExpandManualPhaseFence、
orchestration codegen)通过临时的 SubmitToCallView 检视 Submit,该 view 把
Submit::deps_ 合成为一个 attrs["manual_dep_edges"] 条目。该 attrs 编码
仅存在于 view 中:它永远不会落到 IR 的 Call 节点上,并且
ManualDepsOnSubmitOnly 结构属性 verifier 会在每个 pass 前后校验这一点。
IterArg - 循环携带值¶
IterArg 扩展 Var,添加 initValue_ 以支持静态单赋值 (SSA) 风格的循环。作用域限定在循环体内,通过 yield 更新,最终值存储在 return_vars 中。
# for i, (sum,) in pl.range(n, init_values=(0,)): sum = pl.yield_(sum + i)
sum_iter = ir.IterArg("sum", ir.ScalarType(DataType.INT64), init_val, span)
for_stmt = ir.ForStmt(i, start, stop, step, [sum_iter], body, [sum_final], span)
语句节点¶
所有 Stmt 子类都从 Stmt 基类继承一个 leading_comments_: vector<string> 元数据字段。详见下文 语句的前导注释。
| 节点类型 | 字段 | 说明 |
|---|---|---|
| AssignStmt | var_ (DefField), value_ (UsualField) |
变量赋值 |
| IfStmt | condition_, then_stmts_, else_stmts_, return_vars_ |
条件分支 |
| ForStmt | loop_var_ (DefField), start_, stop_, step_, iter_args_ (DefField), body_, return_vars_ (DefField), kind_ |
带可选迭代参数的 for 循环 |
| WhileStmt | condition_, iter_args_ (DefField), body_, return_vars_ (DefField) |
带条件和迭代参数的 while 循环 |
| InCoreScopeStmt | name_hint_, body_, split_(可选) |
InCore 区域;由 OutlineIncoreScopes 提取为 Function(InCore) |
| ClusterScopeStmt | name_hint_, body_ |
Cluster 区域;由 OutlineClusterScopes 提取为 Function(Group) |
| HierarchyScopeStmt | name_hint_, body_, level_, role_(可选) |
给定 Level/Role 的流水线阶段区域 |
| SpmdScopeStmt | name_hint_, body_, core_num_(整型 Expr), sync_start_ |
SPMD 启动区域;提取为 Function(Spmd) |
| SplitAivScopeStmt | name_hint_, body_, split_(SplitMode,永不为 None), count_(= 2) |
显式 AIV 切分区域(pl.split_aiv);可嵌套;由 LowerAutoVectorSplit(pass 19)消费并擦除 |
| RuntimeScopeStmt | name_hint_, body_, manual_ |
Orchestrator 运行时区域(PTO2_SCOPE);manual_=true 选择手工依赖模式 |
| YieldStmt | values_ |
在循环迭代中产出值 |
| EvalStmt | expr_ |
为副作用求值表达式 |
| SeqStmts | stmts_ |
通用语句序列 |
| BreakStmt | (无) | 退出循环 |
| ContinueStmt | (无) | 跳至下一次循环迭代 |
| InlineStmt | body_、language_(InlineLanguage) |
目标语言(如 Python)的逐字源码片段。各 pass 视其为叶子节点;用于 HOST SubWorker 函数体 |
语句的前导注释¶
每个 Stmt 都带有一个可选的 leading_comments_: vector<string> 字段,用于保留 Python DSL 中的源码级 # 注释和裸字符串文档字符串(docstring)。打印器会将每一行以 # <text> 的形式输出在该语句上方。
- 构造函数参数(与
span_对称)。 每个Stmt子类的构造函数都在最后增加了leading_comments形参(默认值为{})。反序列化器从字段表中读取"leading_comments",与"span"一起传入构造函数——该字段在构造时即完成初始化,而非事后附加。 - 注册为
IgnoreField。 注释会在二进制序列化(serialize_to_file)中保留,但不参与structural_equal或结构哈希。两个仅在leading_comments_上有差异的语句相等且哈希一致。 - Python 侧只读。
stmt.leading_comments仅暴露为只读。官方的修改通道是自由函数ir.attach_leading_comments(stmt, comments),供解析器构造器和合并注释的 pass 在晚期绑定时使用。 - 解析器附着规则。 对于简单语句,不晚于该语句
end_lineno的注释会被作为前导注释收集——这意味着同一行的尾随注释(y = 1 # note)附着到当前语句本身,而非下一条语句。对于复合语句(for/while/if/with),收集上限为首行行号,以便函数体内部的注释由内部语句自身收集。函数体中任何位置的裸字符串表达式(docstring)都会成为下一条语句的前导注释。 - 块末尾注释。 出现在块中最后一条语句之后(并与块同级缩进)的注释没有合适的附着目标,将被丢弃并发出
UserWarning。将它们移到某条语句之上或外层作用域以保留它们。列信息用于区分真正的块末尾注释和仅仅出现在中间行的外层注释(例如else:前的# fallback)。 - SeqStmts 不变式。
SeqStmts是一个透明容器,不应直接持有leading_comments_;注释始终附着到其内部的(非 Seq)语句上。 - Pass 传递。 重建语句的 IR pass 采用
MutableCopy(op)+ 字段赋值——副本会自动保留leading_comments_以及其他所有未改动的字段。当一个 pass 将一条语句拆分为多条时(例如expand_mixed_kernel将InCore调用拆为 AIC + AIV),通过std::make_shared<NewT>(..., orig->leading_comments_)构造第一条新语句,使原语句的注释附着到第一条发出的语句上。当一个 pass 删除一条复合语句时(例如unroll_loops消除ForStmt),其注释通过AttachLeadingComments转移到第一条留存的 body 语句上。
# DSL
"""cache intermediate"""
# reuse later
y = x + 1 # for performance
# Parsed
# AssignStmt.leading_comments == ["cache intermediate", "reuse later", "for performance"]
# Printed
# cache intermediate
# reuse later
# for performance
y: f32 = x + 1
ForStmt 详细说明¶
# Without iter_args: for i in pl.range(10): x = x + i
for_stmt = ir.ForStmt(i, start, stop, step, [], body, [], span)
# With iter_args: for i, (sum,) in pl.range(10, init_values=(0,)): sum = pl.yield_(sum + i)
for_stmt = ir.ForStmt(i, start, stop, step, [sum_iter], body, [sum_final], span)
注意: DSL 接受简写形式
pl.range(stop)/pl.range(start, stop)作为语法糖(类似 Python 的range())。IR 始终存储三个字段(start_、stop_、step_);解析器填充默认值(start=0, step=1),打印器在匹配时省略它们。
WhileStmt 详细说明¶
# Natural: while x < 10: x = x + 1
while_stmt = ir.WhileStmt(condition, [], body, [], span)
# SSA form: for (x,) in pl.while_(init_values=(0,)): pl.cond(x < 10); x = pl.yield_(x + 1)
while_stmt = ir.WhileStmt(condition, [x_iter], body, [x_final], span)
属性: condition_ 每次迭代都会求值;支持 SSA iter_args/return_vars;DSL 使用 pl.cond() 作为第一条语句。
- 不带 iter_args 的自然语法通过 ConvertToSSA Pass 转换为 SSA
- 存在 iter_args 时,循环体必须以 YieldStmt 结尾,并且尾部之前的位置不允许再出现 YieldStmt——yield 是作用域的终结语句。同一规则同样适用于
return_vars_非空的 ForStmt / IfStmt。在 SSA 形式下由SSAVerify强制(参见99-verifier.md,错误码MISPLACED_YIELD)。
ScopeStmt 详细说明¶
ScopeStmt 是一个抽象基类,用于标记具有特定执行上下文的区域。下列五个具体子类
各自只携带其类型有效的字段——非法组合在构造时即不可表达。在 ScopeStmt 类型的引用上,
可使用 s.scope_kind(C++ 中为 s.GetScopeKind())来取回类型,或使用
isinstance(s, InCoreScopeStmt) 在具体类型上分派。
五个子类共享公共基类字段 name_hint_: str 和 body_: StmtPtr。注意:
pl.at(level=Level.CORE_GROUP) 实际下沉到 InCoreScopeStmt,
而非 HierarchyScopeStmt——解析器会在 CORE_GROUP
拒绝 role=。HierarchyScopeStmt 仅用于非 CORE_GROUP 的层级
(host、cluster、global),并不是 in-core 作用域的通用替代。
# with pl.at(level=Level.CORE_GROUP): y = pl.add(x, x)
in_core = ir.InCoreScopeStmt(name_hint="", body=body, span=span)
# with pl.cluster():
cluster = ir.ClusterScopeStmt(name_hint="", body=body, span=span)
# with pl.at(level=Level.HOST, role=Role.SubWorker):
hier = ir.HierarchyScopeStmt(level=ir.Level.HOST, role=ir.Role.SubWorker,
name_hint="", body=body, span=span)
# with pl.spmd(8):
spmd = ir.SpmdScopeStmt(core_num=ir.ConstInt(8, DataType.INDEX, span),
sync_start=False, name_hint="", body=body, span=span)
# for aiv_id in pl.split_aiv(2, mode=pl.SplitMode.UP_DOWN): (显式 AIV 切分区域)
split_aiv = ir.SplitAivScopeStmt(split=ir.SplitMode.UP_DOWN, count=2,
name_hint="", body=body, span=span)
# with pl.manual_scope(): (orchestrator 运行时区域,使用手工依赖模式)
runtime = ir.RuntimeScopeStmt(manual=True, name_hint="", body=body, span=span)
# for i in pl.spmd(8): # loop-style 语法糖
# offset = i * 64
# tile = pl.load(a, [offset, 0], [64, 128])
# ...
# 解析器会将此 for-loop 脱糖为:
# SpmdScopeStmt(body=InCoreScopeStmt(body=[i = tile.get_block_idx(); ...]))
# 这样块索引 `i` 就在隐式的 InCore 区域里被绑定。随后
# `OutlineIncoreScopes` + `OutlineClusterScopes` 会把 InCore 体提取为
# 合成的 `Function(InCore)`,并把 Spmd 包装提取为 `Function(Spmd)`,
# 行为与 `with`-form 单内核调用路径一致。
属性:
- 所有作用域语句对 SSA 透明(无 iter_args/return_vars),且不是控制流 (执行一次,线性执行)。
- 必填字段在构造时强制校验:
HierarchyScopeStmt.level_不可为空;SpmdScopeStmt.core_num_为非空ExprPtr。表达式可以是任何整型 IR 值——Simplify会折叠闭包算术为ConstInt,codegen 则按闭合函数作用 域解析Var引用。 InCoreScopeStmt是pl.at(level=Level.CORE_GROUP)的下沉目标; 解析器会在CORE_GROUP拒绝role=,因此HierarchyScopeStmt仅用于其它层级。- Pass 行为:
OutlineIncoreScopes将InCoreScopeStmt提取为Function(InCore)OutlineClusterScopes将ClusterScopeStmt提取为Function(Group), 将独立的SpmdScopeStmt提取为Function(Spmd)OutlineHierarchyScopes提取HierarchyScopeStmtSplitAivScopeStmt不被提取:它对 SSA 与各 outliner 透明(保留在被 提取出的Function(InCore)体内),随后由LowerAutoVectorSplit(pass 21)消费并擦除。它永不到达ExpandMixedKernel(pass 20)或 codegen——下游只看到逐算子的aiv_shard/aic_gather/tpush/tpop标记;若有SplitAivScopeStmt残留到此,PTO codegen 守卫会显式 报错。SplitAivScopeStmt可嵌套:经由通用的BeginScope/EndScope构建, 可置于任意父上下文(pl.range/pl.pipeline循环或if)。同级区域可 携带不同的split_模式(多模式);pass 21 的减半是按区域局部进行 的,因此每个区域独立减半,区域外的向量计算保持全宽。顶层for aiv_id in pl.split_aiv(...)会被 parser 包裹在外层InCoreScopeStmt中(以便OutlineIncoreScopes提取),即InCoreScopeStmt{ body: SplitAivScopeStmt{...} }。- 对于
RuntimeScopeStmt(manual=true)内的每个pl.submit(kernel, ..., deps=[tid1, tid2]),parser 发出一个Submit节点,并把用户deps=kwarg 直接填入其一等的deps_字段(每项为Scalar[TASK_ID]—— 由 先前pl.submit(...)返回的 producer TaskId、TaskId 循环 iter_arg,或 字面量None,None会被丢弃)。Submit在整个流水线中保持不变; orchestration codegen 通过临时的SubmitToCallView读取它——该 view 把Submit::deps_合成为attrs["manual_dep_edges"](仅 view 内部存在, 永不落到 IR 的普通Call上,由 ManualDepsOnSubmitOnly verifier 保证)—— 然后填充一个定长栈数组,并对每个 task 发出一次params.set_dependencies(arr, count)调用。 RuntimeScopeStmt在manual=false时下沉为PTO2_SCOPE(),在manual=true时下沉为PTO2_SCOPE(PTO2ScopeMode::MANUAL)。它由pl.manual_scope()(manual 模式)和 orchestration codegen 路径 (auto 模式)创建;不会被独立外提为函数。
变换示例:
# Before: with pl.at(level=Level.CORE_GROUP): y = pl.add(x, x); return y
# After: main_incore_0(x) -> y; main(x): y = main_incore_0(x); return y
并行 for 循环 (ForKind):
# for i in pl.parallel(10): ...
for_stmt = ir.ForStmt(i, start, stop, step, [], body, [], span, ir.ForKind.Parallel)
kind_ 字段(ForKind 枚举)区分顺序执行(ForKind.Sequential,默认)、并行执行(ForKind.Parallel)、编译时展开(ForKind.Unroll)和软件流水线(ForKind.Pipeline)的循环。在 DSL 中,pl.range() 生成顺序循环,pl.parallel() 生成并行循环,pl.unroll() 生成编译时展开循环,pl.pipeline(N, stage=F) 生成软件流水线循环。打印器相应输出 pl.parallel(...)、pl.unroll(...) 或 pl.pipeline(..., stage=F)。ForKind.Pipeline 是临时标记:LowerPipelineLoops 将循环体复制 F 份并保留该 kind 作为作用域标记,随后 CanonicalizeIOOrder 重排循环体 IO 并把 kind 降回 Sequential。
要求:
- yield 的值数量 = IterArgs 数量
- return_vars 数量 = IterArgs 数量
- IterArgs 仅在循环体内可访问
- return_vars 在循环之后可访问
类型节点¶
| 节点类型 | 字段 | 说明 |
|---|---|---|
| ScalarType | dtype_ |
标量类型(INT64、FP32 等) |
| TensorType | shape_, dtype_, memref_(可选) |
多维张量 (Tensor) |
| TileType | shape_, dtype_, memref_(可选), tile_view_(可选), memory_space_(可选) |
统一缓冲区中的 Tile |
| TupleType | types_ |
类型元组 |
| PipeType | pipe_kind_ |
硬件流水线/屏障 |
| UnknownType | - | 未知或推断类型 |
内存引用 (MemRef)¶
描述张量/Tile 共享的内存分配元数据。对于 Tile,内存空间保存在
TileType.memory_space_;TensorType 的规范内存空间固定为 DDR。
| 字段 | 类型 | 说明 |
|---|---|---|
addr_ |
ExprPtr | 基地址 |
size_ |
size_t | 大小(字节) |
id_ |
uint64_t | 稳定的 MemRef 标识符 |
注意:
ir.Mem是ir.MemorySpace的简写别名。
TileView - Tile 布局¶
描述 Tile 的布局和访问模式:
| 字段 | 类型 | 说明 |
|---|---|---|
valid_shape |
list[ExprPtr] | 有效维度 |
stride |
list[ExprPtr] | 每维步长 |
start_offset |
ExprPtr | 起始偏移量 |
tile_view = ir.TileView()
tile_view.valid_shape = [ir.ConstInt(16, DataType.INT64, span)] * 2
tile_view.stride = [ir.ConstInt(1, DataType.INT64, span), ir.ConstInt(16, DataType.INT64, span)]
tile_view.start_offset = ir.ConstInt(0, DataType.INT64, span)
Function 节点¶
# def add(x, y) -> int: return x + y
params = [
ir.Var("x", ir.ScalarType(DataType.INT64), span),
ir.Var("y", ir.ScalarType(DataType.INT64), span)
]
return_types = [ir.ScalarType(DataType.INT64)]
body = ir.AssignStmt(result, ir.Add(params[0], params[1], DataType.INT64, span), span)
func = ir.Function("add", params, return_types, body, span)
# With function type
func_orch = ir.Function("orchestrator", params, return_types, body, span, ir.FunctionType.Orchestration)
| 字段 | 类型 | 说明 |
|---|---|---|
name_ |
string | 函数名称 |
func_type_ |
FunctionType | 函数类型(Opaque、Orchestration、InCore、AIC、AIV、Group 或 Spmd) |
params_ |
list[VarPtr] | 参数变量 (DefField) |
param_directions_ |
list[ParamDirection] | 参数方向,与 params_ 长度相同 |
return_types_ |
list[TypePtr] | 返回类型 |
body_ |
StmtPtr | 函数体 |
level_ |
optional[Level] | 层次级别(对 InCore/AIC/AIV/Group/Orchestration 自动派生,详见下文) |
role_ |
optional[Role] | 层次角色(对 InCore/AIC/AIV/Group/Orchestration 自动派生,详见下文) |
attrs_ |
list[(str, Any)] | 有序的自由形式元数据,以 UsualField 暴露(参与结构遍历) |
level_ / role_ 自动派生¶
当 func_type_ 属于 {InCore, AIC, AIV, Group, Orchestration} 时,
Function 构造函数会在未显式提供 level_ / role_ 时自动派生:
func_type_ |
派生的 level_ |
派生的 role_ |
|---|---|---|
Orchestration |
CHIP |
Orchestrator |
InCore |
CHIP_DIE |
Worker |
AIC |
AIC |
Worker |
AIV |
AIV |
Worker |
Group |
CORE_GROUP |
Worker |
如果显式提供 level_ / role_,其值必须与派生值一致,否则构造时抛出
pypto.ValueError。其他函数类型(Opaque、Spmd)不进行派生,除非
调用方显式设置,否则两个字段保持 nullopt。当 level_ / role_ 存在
时,Python 打印器会在 @pl.function(...) 装饰器上输出 level= / role=
关键字。
抽象(运行时绑定)SubWorker¶
HOST 层的 SubWorker 是运行在 fork 出来的 orchestrator 进程中的纯 Python
回调(其函数体作为 InlineStmt 原样捕获,不按 DSL 解析)。有些回调无法在
编译期写出——例如需要 live 模型状态的采样闭包。将函数体声明为 ... 即把该
SubWorker 标记为抽象的运行时绑定回调点(runtime-bound callback point):
@pl.function(level=pl.Level.HOST, role=pl.Role.SubWorker)
def sample(logits: pl.Tensor[[B, V], pl.FP32]) -> pl.Tensor[[B], pl.INT32]:
... # 实现由运行时提供,而非此处
这会设置 Function.requires_runtime_binding_ = true(一个反射字段,因此可
通过 .pto 序列化与 Python 打印器往返——打印器会重新输出 ... 函数体)。
裸 pass 函数体不是抽象的,它是一个具体的空操作 SubWorker。
影响:
- Codegen 为该 SubWorker 模块生成一个 guard 桩:若在未绑定时被派发会抛
异常(而不是静默空操作),并额外产出
sub_workers/__required__.json清单列出所有抽象名称。 - 运行时要求通过
compiled.prepare(callbacks={"sample": fn})提供实现。 缺少绑定时在prepare()阶段抛出ValueError,而非派发时。 (sub_worker_overrides=是callbacks=的已弃用别名。)
ParamDirection 枚举¶
| 值 | 说明 |
|---|---|
In |
只读输入参数(默认) |
Out |
只写输出参数 |
InOut |
读写输入/输出参数 |
FunctionType 枚举¶
| 值 | 说明 |
|---|---|
Opaque |
未指定的函数类型(默认) |
Orchestration |
运行在主机/AICPU 上,用于控制流和依赖分析 |
InCore |
AICore 子图执行(未特化) |
AIC |
Cube 核心内核(特化的 InCore) |
AIV |
Vector 核心内核(特化的 InCore) |
Group |
AIC + AIV 内核的协调调度组 |
Spmd |
SPMD 数据并行调度封装 |
IsInCoreType(type) / ir.is_incore_type(type) 对 InCore、AIC 和 AIV 返回 True。
Program 节点¶
包含多个函数的容器,具有确定性排序:
| 字段 | 类型 | 说明 |
|---|---|---|
name_ |
string | 程序名称 (IgnoreField) |
functions_ |
map[GlobalVarPtr, FunctionPtr] | 函数的有序映射 |
program = ir.Program([func1, func2], "my_program", span)
add_func = program.get_function("add") # Access by name
函数存储在有序映射中,以确保确定性排序。GlobalVar 名称必须与函数名称匹配。
按类别汇总的节点¶
| 类别 | 数量 | 节点 |
|---|---|---|
| 基类 | 4 | IRNode, Expr, Stmt, Type |
| 变量 | 2 | Var, IterArg |
| 常量 | 3 | ConstInt, ConstFloat, ConstBool |
| 二元运算 | 23 | Add, Sub, Mul, FloorDiv, FloorMod, FloatDiv, Min, Max, Pow, Eq, Ne, Lt, Le, Gt, Ge, And, Or, Xor, BitAnd, BitOr, BitXor, BitShiftLeft, BitShiftRight |
| 一元运算 | 5 | Abs, Neg, Not, BitNot, Cast |
| 调用/访问 | 2 | Call, TupleGetItemExpr |
| 操作 | 2 | Op, GlobalVar |
| 语句 | 16 | AssignStmt, IfStmt, ForStmt, WhileStmt, ReturnStmt, InCoreScopeStmt, ClusterScopeStmt, HierarchyScopeStmt, SpmdScopeStmt, SplitAivScopeStmt, YieldStmt, EvalStmt, SeqStmts, BreakStmt, ContinueStmt, InlineStmt |
| 类型 | 6 | ScalarType, TensorType, TileType, TupleType, PipeType, UnknownType |
| 函数 | 2 | Function, Program |