
NPU的编译器开发:条件执行与分支处理上周五晚上十一点,我在调试一个客户反馈的模型推理异常——一个简单的if-else分支,在ARM CPU上跑得好好的,迁移到NPU后输出全乱。盯着反汇编出来的NPU指令流看了两个小时,发现编译器把条件分支优化成了两条并行路径,但两条路径的中间结果在汇合点发生了数据竞争。这个bug让我意识到,NPU编译器里的分支处理,远没有教科书上写的那么简单。条件执行:NPU的“软跳转”艺术NPU和CPU最大的不同在于,它没有PC寄存器,没有传统的分支预测器。你没法像写汇编那样“cmp r0, #0; beq label”。NPU的指令流是静态调度的,所有执行路径在编译期就必须确定下来。我最早接触NPU条件执行时,犯过一个低级错误:试图用条件码寄存器来实现分支。结果发现NPU的条件码只有4位,而且每个周期只能更新一次。更坑的是,某些NPU架构的条件码是“粘性”的——一旦置位,除非显式清除,否则会一直保持。这导致后续无关指令也被错误地条件化。正确的做法是使用谓词寄存器。每个执行单元(PE)内部有一组谓词寄存器,通常8-16个,每个1位。指令可以指定“如果谓词P0为真,则执行;否则跳过”。这听起来简单,但实际实现时有个关键细节:谓词寄存器的写入本身也是条件化的。如果你写“P0 = (r0 0)”,这条比较指令本身可能被另一个谓词控制。这种嵌套条件执行,在编译器里处理不好就会产生死锁。分支处理的三种策略:我踩过的坑策