跳转至

NPU 内联汇编

bisheng 使用 GNU extended asm。内联的模板由 AI Core 汇编器处理,因此模板中写的是 NPU 指令,不是 host x86/ARM 指令。

无显式输出的 Vector 示例

typedef short __attribute__((address_space(6))) *ubptr;

void inline_vadd(ubptr dst, ubptr src0, ubptr src1,
                 unsigned long config) {
    __asm__ volatile(
        "VADD.s16 [%0], [%1], [%2], %3, MASK"
        :
        : "l"(dst), "l"(src0), "l"(src1), "l"(config)
        : "memory");
}

生成结果:

// begin inline asm
VADD.s16 [X0], [X1], [X2], X3, MASK
// end inline asm
RET

显式输出的 Scalar 示例

unsigned long inline_add(unsigned long a, unsigned long b) {
    unsigned long out;
    __asm__ volatile(
        "ADD.s64 %0, %1, %2"
        : "=l"(out)
        : "l"(a), "l"(b));
    return out;
}

常见分配结果为:

ADD.s64 X0, X0, X1
RET

约束

写法 含义
"l"(value) HiIPU 标量/地址寄存器输入
"=l"(value) 只写输出
"+l"(value) 读写同一个值
%0%1 第 0、第 1 个操作数的汇编占位符
"memory" 通知优化器该 asm 可能读写未显式列出的内存

当前 c220 后端对普通 "r" 约束处理不兼容,可能报告 inline 类型或寄存器类错误。NPU 的地址和 64 位标量操作数应使用已验证的 "l" 约束。

volatilememory

  • 没有 C 可见输出的指令应使用 volatile,避免被删除或合并。
  • 访问 UB、GM、L1、L0 或隐式硬件状态时通常需要 "memory" clobber。
  • "memory" 只约束编译器重排,不会自动插入 AI Core 的事件或屏障。
  • 如果 asm 只做纯标量计算并准确声明所有输入输出,可不使用 "memory"

隐式状态

MASKCMPMASK、SPR、事件槽、Cube 累加状态等无法完全通过普通 C 输入输出表达。相关操作应:

  1. 放在同一个 asm 块中,或用 C 可见依赖连接;
  2. 保留必要的 volatilememory
  3. 不与编译器生成的同步序列交叉重排;
  4. 使用生成汇编确认最终寄存器和顺序。

配置寄存器示例

命名 SPR 也可以通过 "l" 约束读写。下面两段在 dav-c220-vec 上分别生成 MOV X0, CTRLMOV CTRL, X0

unsigned long read_ctrl_inline(void) {
    unsigned long value;
    __asm__ volatile("MOV %0, CTRL" : "=l"(value));
    return value;
}

void write_ctrl_inline(unsigned long value) {
    __asm__ volatile(
        "MOV CTRL, %0"
        :
        : "l"(value)
        : "memory");
}

如果工具链已有对应 builtin,应优先用 builtin 表达状态读写。各寄存器方向和 CPU 限制见 配置寄存器与隐式状态

多行模板

__asm__ volatile(
    "MOVEMASK MASK[0], %0\n\t"
    "MOVEMASK MASK[1], %1\n\t"
    "VADD.s16 [%2], [%3], [%4], %5, MASK"
    :
    : "l"(mask0), "l"(mask1),
      "l"(dst), "l"(src0), "l"(src1), "l"(config)
    : "memory");

同一模板中的指令保持局部顺序,但跨执行域的数据完成仍需要相应事件或屏障。

不要这样写

// 错误:硬编码参数寄存器,编译器不知道 X0/X1 被使用
__asm__("ADD.s64 X0, X0, X1");

// 风险:无输出、无 volatile,可能被删除
__asm__("VADD.s16 [%0], [%1], [%2], %3, MASK" : : ...);