Go汇编实战的坑

蒙卓 · · 2495 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

为啥写

Go的汇编一直是我感兴趣的地方,为了验证之前所学的汇编知识和好玩 ,我决定往Go官方提交一个性能patch。 所以到官方的标准库里搜了一圈,发现adler32并没有硬件加速的实现,而Intel已经公布了相关的SSE加速实现 https://github.com/01org/isa-l/

所以我决定把Intel的抄过来,结果不停地掉坑和爬出来,终于提交了patch(撒花)

https://go-review.googlesource.com/c/51850

希望在Go 1.10发布的时候能进入官方源:)

  1. 写法的区别
  2. Go汇编不支持
  3. 难以调试
  4. 内存越界与LEA
  5. 向官方提交代码需要注意的地方,保证Change ID一致

坑一:Intel和AT&T的写法的区别

Intel写法是: opcode source destination

AT&T 也就是Go官方汇编语言的写法正好是反过来的: opcode destination source

抄代码的时候有点绕

坑二 Go汇编不支持:

Go的维护者们对于新添加汇编opcode一直是很保守的,比如2001年前后就有的SSE2 里的PSHLLW()竟然不支持(= =||)。 所以得自己填BYTE,比如官方文档中的MOVQ用BYTE方式编写 https://golang.org/doc/asm

BYTE $0x0f; BYTE $0x6f; BYTE $0x00 // MOVQ (AX), M0

但这里就有一个坑,比如PSHLLD和PSHRQ的Opcode是一样的……只是按/r 寄存器类型进行区别。需要注意

坑三:调试困难

一般代码测试时,都可以直接输出日志,帮助定位问题,但是汇编不行,所以我是通过把需要的值放入某个不用的寄存器, 例如

#define debug R15
// min(a, b int) int
TEXT min(SB), NOSPLIT, $0

在需要的时候,提前把函数返回

MOVQ debug, ret+16(FP)
RET

当然应该有更好的方法,可以把所有寄存器打印出来,不过这个方法够我自己调试用了。

坑四:内存越界与LEA

一般程序中都是指针,或者直接结构体。不过,汇编这里回归本真,只有内存地址和寄存器。所以一定要小心访问数据的边界和跳转的内存地址。 这里学到了一个LEA的用法,地址计算器,把内存地址到目标寄存器里。

LEAQ  0(data)(size*1), end

具体用法

  • 0: 写死的偏移量
  • data: 偏移值
  • size: 动态偏移量,可以用乘法

坑五:

提交代码到gerrit要保持Change ID相同,要不然算一个新的Change。

附录:opcode坑

  • MOVOU: O=oct, U=unaligned, 指的是八个word,即128位,用于XXM寄存器的移动。
  • TESTQ: 对比值,并影响FLAG
  • DIVL:低位除,如果高位有数据就会出错

资料: Go官方介绍: https://golang.org/doc/asm


有疑问加站长微信联系(非本文作者)

本文来自:Zhuo's blog

感谢作者:蒙卓

查看原文:Go汇编实战的坑

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2495 次点击  
加入收藏 微博
1 回复  |  直到 2020-05-08 12:22:43
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传