Go汇编用于关键路径性能优化,需Go文件声明函数原型、汇编文件用Unicode中点命名、FP/SP偏移严格按调用约定,反汇编验证是否生效。

Go 语言里写汇编不是为了炫技,而是当 pprof 显示某个函数占了 30% CPU、且已无算法优化空间时,才值得动手。它不提供“加速 10 倍”的魔法,但能帮你把关键路径压到最薄——前提是每个符号、每个偏移、每个伪寄存器都对得上。
汇编函数报 undefined 是最常见的拦路虎
这不是链接失败,是 Go 编译器压根没认出你要导出的符号。根本原因就一条:Go 文件里没声明函数原型,或者声明了但签名/名字/包名和汇编文件不一致。
-
add.go中必须有func Add(a, b uint64) uint64这行(哪怕函数体为空),否则go build不会去 .s 文件里找实现 -
add_amd64.s的函数名必须是TEXT ·Add(SB), $0-24——注意那个·是 Unicode 中点(U+00B7),不是英文句点;$0-24表示栈帧大小为 0,参数+返回值共 24 字节(两个uint64输入 + 一个uint64输出) - 文件必须在同一包、同一目录下,不能放在
internal/或子目录里;GOOS/GOARCH要匹配,比如amd64机器不能用_arm64.s
FP 和 SP 的偏移规则必须手算,不能靠猜
Go 汇编不用真实寄存器传参,而是通过帧指针 FP 加偏移访问参数,靠的是调用约定。64 位下,参数从 0(FP) 开始,每个占 8 字节,返回值紧接在最后参数之后。
- 函数
func Max(a, b int64) int64:第一个参数a在0(FP),b在8(FP),返回值在16(FP) - 局部变量要用
SP,比如MOVQ $42, -8(SP)把常量存到栈顶下方 8 字节处;但前提是函数声明里写了栈帧大小,例如$0-16表示不分配额外栈空间,$16-16才表示分配 16 字节本地空间 - 误用
0(SP)当作第一个参数?程序可能跑通但行为随机——因为SP指向当前栈顶,而参数在调用者栈帧里,不在你自己的栈空间内
怎么验证汇编代码真被用了
别信输出日志,要亲眼看到符号加载和指令执行。最直接的办法是反汇编可执行文件,确认你的函数体是否被替换成手写的指令。
立即学习“go语言免费学习笔记(深入)”;
- 先构建:
go build -o myapp ./main.go - 再反汇编:
go tool objdump -s "main\.Add" ./myapp(注意函数名带包路径,且需转义点号) - 如果输出里出现你写的
MOVQ、ADDQ,说明成功;如果还是显示CALL runtime.morestack_noctxt等 Go 运行时指令,说明汇编没生效,回去检查.s文件名、·符号、函数签名三者是否完全一致 - 顺带一提:
go tool compile -S main.go输出的是 Go 编译器生成的中间汇编,不是你手写的;它只能帮你理解 Go 的调用惯例,不能验证你的 .s 是否接入
真正难的从来不是写几条 MOVQ,而是确保 Go 的调用协议、栈帧布局、GC 标记(FUNCDATA)、以及跨平台兼容性全部对齐。一个 · 打成英文点,或一个偏移错 1 字节,就会让整个函数静默失效——它不报错,只是默默走 Go 的默认实现。


















