背景
当前 PTODSL/PTOAS 中同时存在三套容易混淆的概念:
scalar.xxx:面向 DSL 用户的运行时标量语义;
pto.xxx:既包含底层 PTO ISA,也包含 SIMT 执行模型操作;
PTO_SimtOp / SimtOpInterface:同时被用于表示 SIMT 状态依赖、
Vector section 归属和历史上的 SIMT 指令集合。
因此目前存在以下问题:
- 相同数学语义在
scalar.xxx 和 pto.xxx 中重复;
- 标准 LLVM 数学操作被包装成 SIMT 专用 PTO op;
- 仅类型或 packed ABI 特殊的操作被误认为执行模型专用;
- 真正依赖 lane/thread 状态的操作与普通纯值运算使用相同 interface;
scalar.max/min 使用 arith.maximumf/minimumf,而底层实际希望采用更高效的
LLVM maxnum/minnum;
pto.load/store 继承 PTO_SimtOp,但 verifier 又允许其在非 SIMT 范围使用;
- section pass 通过
SimtOpInterface 推断 Vector/SIMT 属性,导致 interface
同时承担语义、执行域和 section 分类职责;
- 文档按照命名空间分章,却没有明确普通语义、目标语义和执行域语义的区别。
相关任务:
本 issue 不以消灭所有 scalar.xxx 或 pto.xxx 为目标,而是重新明确 Scalar、
SIMT、target-specific 和底层 PTO 接口的职责边界。
目标
- 相同语义只保留一条推荐路径,避免普通函数和 SIMT entry 各维护一套实现;
- 标准数学语义优先落到标准 LLVM operation/intrinsic;
- 只有真正依赖 SIMT 执行状态的操作才使用
SimtOpInterface;
- 具有 rounding、saturation、cache policy、packed ABI 或 HiVM intrinsic 等
特化语义的接口允许保留独立 pto.xxx;
- target-specific 与 SIMT-only 分开建模,不能因为操作对应目标 intrinsic 就自动
将其归为 SIMT;
- section pass、verifier、LLVM emitter、文档和测试使用同一套分类结果;
- 保持手写 VPTO 的兼容性,并为需要迁移的旧 surface API 提供清晰策略。
设计原则
1. 标准语义优先使用标准 LLVM 表达
如果一个操作具有明确、稳定的 LLVM 语义,并且不依赖 SIMT 执行状态,则不应仅因
历史原因被定义为 SIMT 专用操作。
典型操作包括:
add/sub/mul/div
comparison
bitwise
select
abs/sqrt/exp/log/pow
max/min
这些操作可以继续经过 arith/math lowering;如果标准 dialect 的语义会引入项目
不需要的额外处理,则允许直接选择更适合目标的 LLVM intrinsic。例如浮点
max/min 统一采用 llvm.maxnum/minnum,而不是
arith.maximumf/minimumf 的 NaN 传播语义。
2. 真正依赖 SIMT 状态的操作保持 SIMT 专用
只有依赖以下信息或行为的操作才应携带 SimtOpInterface:
- thread、lane、block、grid 状态;
- 跨 lane 通信和 workitem collective;
- SIMT 同步和内存可见性;
- SIMT entry 间状态保存;
- 仅在 SIMT 执行环境中有定义的访存或原子操作。
此类操作继续使用显式 pto.xxx 接口,并在普通函数中给出明确诊断。
3. 有特化语义的接口允许保留两套
两套接口本身不是问题。只有语义相同却实现分叉时,才需要统一。以下接口可以同时
存在:
scalar.cast vs pto.convert
scalar.load/store vs pto.ldg/stg
普通 LLVM fma vs HiVM/packed pto.fma
标准数学操作 vs 具有目标类型或 packed ABI 限制的 PTO 操作
前提是接口名称、支持类型、rounding、saturation、cache policy、packed ABI 和
执行域限制足够明确。
4. SimtOpInterface 只表达执行模型依赖
不能再使用 SimtOpInterface 表达:
- 操作位于 SIMT 文档章节;
- 操作最终生成 Vector 指令;
- 操作支持 packed 类型;
- 操作对应某个 target intrinsic。
执行域、target-specific、数据类型/ABI 和 section kind 是不同维度。
操作分类与处理策略
A. 普通 Scalar 运算
包括 arithmetic、comparison、bitwise、select 和 scalar.cast。
处理策略:
- 保持
scalar API;
- 继续使用标准 MLIR/LLVM 类型和操作;
- 不增加无额外语义的同名
pto.xxx;
- signedness 必须在转成 signless LLVM carrier 前确定;
index 继续用于现有地址、循环和动态 shape 计算;
- 普通 scalar 算术不携带
SimtOpInterface。
B. max/min
推荐用户接口统一为:
scalar.max(lhs, rhs)
scalar.min(lhs, rhs)
类型和 lowering 规则:
| 类型 |
目标语义 |
| float |
llvm.maxnum/minnum |
| signed integer |
llvm.smax/smin |
| unsigned integer |
llvm.umax/umin |
| index |
保留现有 cmp + select |
浮点路径不再使用 arith.maximumf/minimumf。项目底层以 LLVM intrinsic 为核心,
而 arith.maximumf/minimumf 的 NaN 传播和零号规则可能引入额外处理,不符合本任务
的性能目标。
现有 pto.fmax/fmin:
- 与新的
scalar.max/min 采用相同的 maxnum/minnum 语义;
- 不再描述为独立的 SIMT 数学语义;
- 作为手写 VPTO 和旧 PTODSL 代码的兼容接口保留;
- 后续可以 canonicalize 到公共
pto.max/min,或明确标记为低层兼容接口;
- 不应仅因名称或历史分组而参与 SIMT section 分类。
测试需要固定普通值、单侧 NaN、双侧 NaN、+0/-0、+inf/-inf、signed、
unsigned、index、普通 AICore 和 SIMT entry 行为。
C. 标准浮点数学
包括:
普通标量推荐接口为 scalar.xxx。其中 scalar.pow 当前缺失,是否补充由实际需求
决定。普通 scalar 类型优先使用以下标准 LLVM 语义:
llvm.fabs
llvm.sqrt
llvm.exp
llvm.log
llvm.pow
现有 pto.absf/sqrt/exp/log/pow:
- scalar 类型上的语义若与 LLVM 完全相同,则不再作为推荐的 SIMT surface;
- 手写 VPTO 继续支持,保证兼容;
- packed 类型若只能通过 PTO op 表达,则保留相应低层接口;
- 支持 packed 类型不能作为携带
SimtOpInterface 的充分理由;
- packed 版本是否允许在普通函数使用,需要依据后端能力单独验证。
这里需要区分“语义公共”和“当前目标类型支持范围”,不能把所有低层类型限制都归为
SIMT 语义。
D. 舍入与 FMA
包括:
pto.ceil
pto.floor
pto.rint
pto.round
pto.fma
当前 lowering 使用:
llvm.hivm.ceil.*
llvm.hivm.floor.*
llvm.hivm.rint.*
llvm.hivm.round.*
llvm.hivm.ffma.*.rrr
因此暂时保留显式 pto.xxx,并逐项确认:
- 与标准 LLVM
ceil/floor/rint/round/fma 是否完全同义;
- NaN、Inf、signed zero 和 rounding 行为;
fma 是否保证 fused semantics;
- scalar 与 packed 类型的支持差异;
- 普通函数是否支持对应 HiVM intrinsic;
- 是否只在 SIMT entry 中合法。
如果语义完全相同且普通函数支持,可以后续增加统一 scalar.xxx;否则继续保持两套
接口,并在名称和文档中明确其目标特化语义。
E. 转换操作
普通转换:
scalar.cast(value, dtype)
用于标准类型转换,不携带显式硬件控制。
特化转换:
pto.convert(
value,
dtype,
rounding=...,
saturation=...,
signedness=...,
)
pto.convert 涉及 rounding、saturation、signedness、低精度格式、packed carrier
和目标 ABI,因此继续保留独立接口,不与 scalar.cast 合并。需要补充 verifier,
明确哪些属性组合和类型对合法。
F. 普通访存与目标访存
普通访问的 surface 保持统一:
scalar.load(ptr, offset)
scalar.store(value, ptr, offset)
但 IR 应根据 pointer domain 分层,而不是全部直接生成 LLVM dialect。按照 #578 评论区
已经形成的讨论结论:
| 访问对象 |
PTODSL 生成的 IR |
原因 |
| GM/UB,单元素 |
pto.load/store |
保留 !pto.ptr<T, space> 和 PTO 地址域 |
GM/UB,contiguous=N |
vector-typed pto.load/store |
保留 PTO pointer domain,并统一连续向量访问 |
| local fragment/栈 |
llvm.load/store |
对象由 llvm.alloca 创建,类型为 !llvm.ptr |
| 最终 LLVM IR |
LLVM load/store |
由 PTOAS LLVM emitter 统一 lowering |
因此 #578 的正确方向不是让所有 scalar.load/store 都直接生成 LLVM dialect,而是:
- GM/UB 访问统一停留在 PTO pointer domain;
- 扩展或复用
pto.load/store 支持连续向量类型;
- local fragment/栈访问保持 LLVM pointer domain;
- 不为使用
llvm.load/store 而提前将 !pto.ptr 转成 !llvm.ptr;
- 最终在 PTOAS LLVM emitter 中完成地址空间和 LLVM load/store lowering。
带 cache policy 的目标访问继续使用:
pto.ldg(...)
pto.stg(...)
并保留 L1/L2 cache policy、GM 地址空间限制、HiVM intrinsic 和实际需要的 SIMT
执行域约束。
当前 pto.load/store 虽继承 PTO_SimtOp,verifier 却允许普通函数使用,这仍然
是需要修正的分类矛盾:
pto.load/store 表达 PTO pointer domain 上的普通访存,不应仅因处理 scalar
value 而被当作 SIMT-only;
pto.load/store 是否参与 section 分类应由地址域、所在函数和实际执行要求决定;
pto.ldg/stg 表达 cache/目标特化访存,并依据实际执行要求决定是否 SIMT-only;
- 手写 VPTO 的现有
pto.load/store 继续兼容。
G. Atomic
包括:
atomic_exch
atomic_add/sub
atomic_min/max
atomic_and/or/xor
atomic_cas
Atomic 具有内存副作用、地址空间限制、cache policy、signedness、packed 类型限制和
目标原子语义,因此继续保留 pto.xxx,不属于普通 Scalar Math 的统一范围。
需要逐项确认当前 atomic 是否都必须位于 SIMT entry,不能仅凭历史 trait 推断。
H. SIMT Query
包括 thread、block、grid、lane、lanemask、clock 和 active mask 等查询操作。
这些操作直接读取执行状态,必须保留:
pto.xxx
PTO_SimtOp
SimtOpInterface
普通函数中拒绝
I. Collective 与跨 Lane 操作
包括:
vote_*
shuffle_*
redux_*
simt_allreduce_*
它们依赖 lane 拓扑和 workitem 集合,继续保持 SIMT-only。
软件 allreduce 内部的 combine 操作遵循公共 scalar 语义:
sum -> 普通 add
max -> LLVM maxnum
min -> LLVM minnum
Collective 是 SIMT 专用的,但 combine 的纯值语义应复用公共 Scalar 规则。
J. 同步和状态操作
包括:
syncthreads
threadfence
threadfence_block
keep
resume
simt_launch
这些操作不是纯值运算,应继续使用 PTO_SimtOp、SimtOpInterface、对应的
MemoryEffects 和 verifier。普通函数误用必须给出稳定诊断。
K. Target-specific Scalar Ops
包括:
它们虽然处理 scalar value,但直接对应 HiVM intrinsic,因此继续使用显式
pto.xxx,不提供通用 scalar.xxx 别名。
是否携带 SimtOpInterface 应由“是否只能在 SIMT 执行域运行”决定,而不是由“是否
target-specific”决定。若普通 AICore 也能执行,应通过 target capability/verifier
表达限制。
Trait 与 Pass 调整
至少需要明确以下三个维度:
| 机制 |
表达内容 |
SimtOpInterface |
操作依赖 SIMT 执行模型 |
MicroOpInterface |
PTO 底层微操作 |
| target capability/verifier |
操作依赖特定 target intrinsic |
不建议仅为分类增加大量新 interface。只有多个 pass 确实需要统一识别时,再增加类似
TargetSpecificOpInterface 的机制。
section pass 应调整为:
- SIMT query、collective、sync、state 可以证明函数属于 SIMT/Vector;
- 普通 scalar math 不单独决定 section kind;
- target-specific scalar op 不自动决定 section kind;
- 优先使用 enclosing function、显式 section 和数据流确定 section;
pto.load/store 不能因为历史上的 SimtOpInterface 被误判。
分阶段实施
阶段 0:形成分类基线
建立完整清单,记录:
surface API
VPTO op
trait/interface
支持类型
允许执行域
LLVM intrinsic
语义差异
副作用
packed ABI
现有测试
迁移结论
每个操作必须得到以下结论之一:
保持公共 scalar
保持 SIMT-only
保持 target-specific
保留双接口
兼容后废弃
需要进一步验证
阶段 1:完成 max/min 闭环
- 浮点统一为 LLVM
maxnum/minnum;
- 整数统一为
smax/smin、umax/umin;
- index 保留
cmp + select;
scalar.max/min 可同时用于普通函数和 SIMT entry;
- 更新 allreduce combine;
pto.fmax/fmin 转为兼容接口;
- 删除
arith.maximumf/minimumf 性能路径。
阶段 2:整理公共浮点数学
逐项处理 abs/sqrt/exp/log/pow,确认 scalar 和 packed 类型的后端支持范围,移除
不必要的 SIMT 分类,同时保持手写 VPTO 兼容。
阶段 3:审计特化运算
逐项验证:
ceil/floor/rint/round/fma
convert
prmt/mulhi/mul_i32toi64
明确它们属于标准语义、目标特化语义、SIMT-only,还是 target-specific 但执行域
无关。
阶段 4:整理访存和 Atomic
阶段 5:修正 section 架构
SimtOpInterface 只保留在真正 SIMT-dependent op 上;
- section pass 不通过普通 Scalar Math 推断 SIMT;
- 增加普通函数、SIMT entry、tile helper 的架构回归测试。
阶段 6:文档和兼容清理
文档按语义层次组织:
Scalar operations
SIMT execution operations
Target-specific scalar intrinsics
Memory and atomic operations
不再把所有处理 scalar value 的操作都放进 SIMT Scalar Math。旧接口需要明确标记为
继续推荐、低层兼容、deprecated 或仅供手写 VPTO 使用。
测试要求
每类迁移需要覆盖:
| 层次 |
覆盖内容 |
| PTODSL |
推荐 surface 生成预期 IR,普通函数和 SIMT entry 行为一致 |
| VPTO |
ODS、verifier、类型、属性和 assembly round-trip |
| Pass |
section normalization/materialization 不误分类 |
| LLVM |
两套 emitter 的 intrinsic 和类型映射一致 |
| Runtime |
普通值、边界值、NaN/Inf、signed zero、rounding、saturation |
| Packed |
每种明确支持的 packed carrier/ABI |
| Negative |
不支持类型、执行域、属性组合和 target capability 的诊断 |
验收标准
非目标
- 不要求一次性删除所有旧接口或手写 VPTO op;
- 不把 SIMT query、collective、sync、state 强行推广到普通函数;
- 不因为形式统一而抹平 rounding、saturation、cache policy 或 packed ABI 差异;
- 不在缺少后端验证时批量扩大支持类型;
- 不要求普通函数与 SIMT entry 生成完全相同的机器指令,只要求公共语义一致且目标代码
合法;
- 不把所有 target-specific op 都定义成 SIMT-only。
/cc @mouliangyu
背景
当前 PTODSL/PTOAS 中同时存在三套容易混淆的概念:
scalar.xxx:面向 DSL 用户的运行时标量语义;pto.xxx:既包含底层 PTO ISA,也包含 SIMT 执行模型操作;PTO_SimtOp/SimtOpInterface:同时被用于表示 SIMT 状态依赖、Vector section 归属和历史上的 SIMT 指令集合。
因此目前存在以下问题:
scalar.xxx和pto.xxx中重复;scalar.max/min使用arith.maximumf/minimumf,而底层实际希望采用更高效的LLVM
maxnum/minnum;pto.load/store继承PTO_SimtOp,但 verifier 又允许其在非 SIMT 范围使用;SimtOpInterface推断 Vector/SIMT 属性,导致 interface同时承担语义、执行域和 section 分类职责;
相关任务:
max/min与 Scalar Math 接口统一方案 #582:max/min与 Scalar Math 接口方案;scalar.load/store的访存 lowering 为 LLVMload/store#578:统一scalar.load/store的普通访存 lowering。本 issue 不以消灭所有
scalar.xxx或pto.xxx为目标,而是重新明确 Scalar、SIMT、target-specific 和底层 PTO 接口的职责边界。
目标
SimtOpInterface;特化语义的接口允许保留独立
pto.xxx;将其归为 SIMT;
设计原则
1. 标准语义优先使用标准 LLVM 表达
如果一个操作具有明确、稳定的 LLVM 语义,并且不依赖 SIMT 执行状态,则不应仅因
历史原因被定义为 SIMT 专用操作。
典型操作包括:
这些操作可以继续经过
arith/mathlowering;如果标准 dialect 的语义会引入项目不需要的额外处理,则允许直接选择更适合目标的 LLVM intrinsic。例如浮点
max/min统一采用llvm.maxnum/minnum,而不是arith.maximumf/minimumf的 NaN 传播语义。2. 真正依赖 SIMT 状态的操作保持 SIMT 专用
只有依赖以下信息或行为的操作才应携带
SimtOpInterface:此类操作继续使用显式
pto.xxx接口,并在普通函数中给出明确诊断。3. 有特化语义的接口允许保留两套
两套接口本身不是问题。只有语义相同却实现分叉时,才需要统一。以下接口可以同时
存在:
前提是接口名称、支持类型、rounding、saturation、cache policy、packed ABI 和
执行域限制足够明确。
4.
SimtOpInterface只表达执行模型依赖不能再使用
SimtOpInterface表达:执行域、target-specific、数据类型/ABI 和 section kind 是不同维度。
操作分类与处理策略
A. 普通 Scalar 运算
包括 arithmetic、comparison、bitwise、select 和
scalar.cast。处理策略:
scalarAPI;pto.xxx;index继续用于现有地址、循环和动态 shape 计算;SimtOpInterface。B.
max/min推荐用户接口统一为:
类型和 lowering 规则:
llvm.maxnum/minnumllvm.smax/sminllvm.umax/umincmp + select浮点路径不再使用
arith.maximumf/minimumf。项目底层以 LLVM intrinsic 为核心,而
arith.maximumf/minimumf的 NaN 传播和零号规则可能引入额外处理,不符合本任务的性能目标。
现有
pto.fmax/fmin:scalar.max/min采用相同的maxnum/minnum语义;pto.max/min,或明确标记为低层兼容接口;测试需要固定普通值、单侧 NaN、双侧 NaN、
+0/-0、+inf/-inf、signed、unsigned、index、普通 AICore 和 SIMT entry 行为。
C. 标准浮点数学
包括:
普通标量推荐接口为
scalar.xxx。其中scalar.pow当前缺失,是否补充由实际需求决定。普通 scalar 类型优先使用以下标准 LLVM 语义:
现有
pto.absf/sqrt/exp/log/pow:SimtOpInterface的充分理由;这里需要区分“语义公共”和“当前目标类型支持范围”,不能把所有低层类型限制都归为
SIMT 语义。
D. 舍入与 FMA
包括:
当前 lowering 使用:
因此暂时保留显式
pto.xxx,并逐项确认:ceil/floor/rint/round/fma是否完全同义;fma是否保证 fused semantics;如果语义完全相同且普通函数支持,可以后续增加统一
scalar.xxx;否则继续保持两套接口,并在名称和文档中明确其目标特化语义。
E. 转换操作
普通转换:
用于标准类型转换,不携带显式硬件控制。
特化转换:
pto.convert涉及 rounding、saturation、signedness、低精度格式、packed carrier和目标 ABI,因此继续保留独立接口,不与
scalar.cast合并。需要补充 verifier,明确哪些属性组合和类型对合法。
F. 普通访存与目标访存
普通访问的 surface 保持统一:
但 IR 应根据 pointer domain 分层,而不是全部直接生成 LLVM dialect。按照 #578 评论区
已经形成的讨论结论:
pto.load/store!pto.ptr<T, space>和 PTO 地址域contiguous=Npto.load/storellvm.load/storellvm.alloca创建,类型为!llvm.ptrload/store因此 #578 的正确方向不是让所有
scalar.load/store都直接生成 LLVM dialect,而是:pto.load/store支持连续向量类型;llvm.load/store而提前将!pto.ptr转成!llvm.ptr;带 cache policy 的目标访问继续使用:
并保留 L1/L2 cache policy、GM 地址空间限制、HiVM intrinsic 和实际需要的 SIMT
执行域约束。
当前
pto.load/store虽继承PTO_SimtOp,verifier 却允许普通函数使用,这仍然是需要修正的分类矛盾:
pto.load/store表达 PTO pointer domain 上的普通访存,不应仅因处理 scalarvalue 而被当作 SIMT-only;
pto.load/store是否参与 section 分类应由地址域、所在函数和实际执行要求决定;pto.ldg/stg表达 cache/目标特化访存,并依据实际执行要求决定是否 SIMT-only;pto.load/store继续兼容。G. Atomic
包括:
Atomic 具有内存副作用、地址空间限制、cache policy、signedness、packed 类型限制和
目标原子语义,因此继续保留
pto.xxx,不属于普通 Scalar Math 的统一范围。需要逐项确认当前 atomic 是否都必须位于 SIMT entry,不能仅凭历史 trait 推断。
H. SIMT Query
包括 thread、block、grid、lane、lanemask、clock 和 active mask 等查询操作。
这些操作直接读取执行状态,必须保留:
I. Collective 与跨 Lane 操作
包括:
它们依赖 lane 拓扑和 workitem 集合,继续保持 SIMT-only。
软件 allreduce 内部的 combine 操作遵循公共 scalar 语义:
Collective 是 SIMT 专用的,但 combine 的纯值语义应复用公共 Scalar 规则。
J. 同步和状态操作
包括:
这些操作不是纯值运算,应继续使用
PTO_SimtOp、SimtOpInterface、对应的MemoryEffects 和 verifier。普通函数误用必须给出稳定诊断。
K. Target-specific Scalar Ops
包括:
它们虽然处理 scalar value,但直接对应 HiVM intrinsic,因此继续使用显式
pto.xxx,不提供通用scalar.xxx别名。是否携带
SimtOpInterface应由“是否只能在 SIMT 执行域运行”决定,而不是由“是否target-specific”决定。若普通 AICore 也能执行,应通过 target capability/verifier
表达限制。
Trait 与 Pass 调整
至少需要明确以下三个维度:
SimtOpInterfaceMicroOpInterface不建议仅为分类增加大量新 interface。只有多个 pass 确实需要统一识别时,再增加类似
TargetSpecificOpInterface的机制。section pass 应调整为:
pto.load/store不能因为历史上的SimtOpInterface被误判。分阶段实施
阶段 0:形成分类基线
建立完整清单,记录:
每个操作必须得到以下结论之一:
阶段 1:完成
max/min闭环maxnum/minnum;smax/smin、umax/umin;cmp + select;scalar.max/min可同时用于普通函数和 SIMT entry;pto.fmax/fmin转为兼容接口;arith.maximumf/minimumf性能路径。阶段 2:整理公共浮点数学
逐项处理
abs/sqrt/exp/log/pow,确认 scalar 和 packed 类型的后端支持范围,移除不必要的 SIMT 分类,同时保持手写 VPTO 兼容。
阶段 3:审计特化运算
逐项验证:
明确它们属于标准语义、目标特化语义、SIMT-only,还是 target-specific 但执行域
无关。
阶段 4:整理访存和 Atomic
scalar.load/store的访存 lowering 为 LLVMload/store#578 评论区结论,让 GM/UB 的单元素及连续向量访问统一使用pto.load/store,local fragment/栈访问使用llvm.load/store;!pto.ptr到 PTOAS LLVM emitter 阶段,再转换为对应 LLVM 地址空间;pto.load/store的 trait 与 verifier 不一致;pto.ldg/stgcache 语义;阶段 5:修正 section 架构
SimtOpInterface只保留在真正 SIMT-dependent op 上;阶段 6:文档和兼容清理
文档按语义层次组织:
不再把所有处理 scalar value 的操作都放进 SIMT Scalar Math。旧接口需要明确标记为
继续推荐、低层兼容、deprecated 或仅供手写 VPTO 使用。
测试要求
每类迁移需要覆盖:
验收标准
scalar.max/min最终使用 LLVMmaxnum/minnum,不再产生额外 NaN 传播逻辑;SimtOpInterface;scalar.cast与pto.convert的语义边界明确;scalar.load/store与pto.ldg/stg的语义边界明确;scalar.xxx还是pto.xxx。非目标
合法;
/cc @mouliangyu