搜索文档搜索当前语言
语言参考
候选版本v0.20260809.0-rc.5

本文档对应当前协同发布候选版本。

语言参考

内联汇编

在遵守 Go 栈和 GC 规则的前提下,使用 Clang 现有的 call-free 内联汇编。

C2Go 支持 Clang 现有的 call-free inline assembly lowering。它不会把内联汇编替换成某个 C2Go 专用函数 API,支持范围也不局限于某个硬编码模板。

空 compiler barrier 可以工作

musl 安全清零代码使用的模式受支持:

static void compiler_barrier(void *pointer) {
    __asm__ __volatile__("" : : "r"(pointer) : "memory");
}

空模板不会发射机器指令。register input 使编译器认为 pointer 仍然存活,memory clobber 阻止内存操作跨过这条 barrier 重排。

它不是硬件 memory fence。如果顺序必须对其他处理器可见,应使用与 target 匹配的 atomic 或 fence 指令。

支持叶子指令

非空的 call-free 指令沿用 Clang 普通 constraint 与 target 机制:

#if defined(__aarch64__)
    __asm__ __volatile__("yield" ::: "memory");
#elif defined(__x86_64__)
    __asm__ __volatile__("pause" ::: "memory");
#endif

源码仍然与 target 相关。应像编写可移植 Clang C 一样,正确保护指令、寄存器 constraint 和 clobber。

Plan 9 输出不一定保留 GNU 汇编助记符:例如当前后端可能把 AArch64 yield 写成 WORD,把 x86 pause 写成 BYTE 序列。验收生成代码时应核对最终编码,不要只搜索源码里的助记符。

为什么必须 call-free

LLVM 把 inline assembly 表示成 CallBase,但受支持的 inline-asm statement 不是机器 CALL 或 BL,不会进入 callee 的 Go morestack prologue。因此 C2Go 将它标为 GC-leaf,不为语句本身创建 safepoint 或 stackmap。

Managed pointer operand 仍是普通 live value。如果指针跨越后面的真实 call 存活,常规 GC relocation 会在后续 inline assembly 使用前生效。

call-free loop 内存在 inline assembly,也不会让循环自动变得 cooperative。启用 loop polling 时,C2Go 仍会插入所需 poll。

不支持或不安全的用法

不要把以下行为隐藏在 inline assembly 中:

  • 调用其他函数,或任何可能增长 goroutine 栈的代码;
  • 未声明的内存访问或寄存器 clobber;
  • 为 managed 栈地址创建长期存在的 native alias;
  • 假设超出 target 生成代码契约的栈对齐;
  • 未经过 target guard 的平台指令。

如果汇编需要调用函数、管理 native stack frame 或暴露稳定 C ABI 入口,应放到显式 unmanaged 原生库边界后面,而不是伪装成叶子 inline assembly。

审核清单

  1. 确认模板不包含 call。
  2. 声明每一个 input、output 与 clobber。
  3. 分别测试每个 target。
  4. 检查生成的 Plan 9 汇编是否包含预期指令编码和正确栈寻址。
  5. 增加端到端 Go 测试,而不只依赖 LLVM IR test。

用于保留和检查编译产物的参数见命令行参考。