Go ASM是一种被Go编译器使用的特殊形式的汇编语言(伪汇编),它基于Plan9输入风格;它是架构独立的,没有所谓的32或64位寄存器。
go汇编寄存器与x86_64对应关系:
AMD64 rax rbx rcx rdx rdi rsi rbp rsp r8 r9 r10 r11 r12 r13 r14 rip
Plan9 AX BX CX DX DI SI BP SP R8 R9 R10 R11 R12 R13 R14 PC
Go ASM中有四个预定义的符号作为伪寄存器(虚拟寄存器,伪寄存器使用时一般需要一个标识符和偏移量为前缀):
伪寄存器SB可以看作是内存的起始地址。foo(SB)
就是foo在内存中的地址,有两种修饰符<>和+N(整数)
:
foo<>(SB)
:表示一个私有元素,只有在同一个源文件中才可以访问;+N
:表示相对地址加上一个偏移量后得到的地址,如foo+8(SB)就指向foo之后8个字节处的地址;Go汇编使用的是caller-save模式,被调用函数的入参参数、返回值都由调用者维护、准备。因此,当需要调用一个函数时,需要先将这些工作准备好,才调用下一个函数,另外这些都需要进行内存对齐,对齐的大小是 sizeof(uintptr)。
伪寄存器FP是一个虚拟帧指针,被用来引用过程参数(由编译器负责维护):
MOVL foo+0(FP),CX
把第一个参数放入到物理上的CX寄存器;MOVL bar+8(FP),DX
把第二个参数放入到DX寄存器中。栈调整(SP寄存器):
SUBQ $24, SP // 为函数分配函数栈帧
...
ADDQ $24, SP // 清除函数栈帧
加减操作:
ADDQ AX, BX // BX += AX
SUBQ AX, BX // BX -= AX
数据搬运:搬运的长度是由MOV的后缀决定
MOVB $1, DI // 1 byte
MOVW $0x10, BX // 2 bytes
MOVD $1, DX // 4 bytes
MOVQ $-10, AX // 8 bytes// 加括号代表是指针的引用
MOVQ (AX), BX // => BX = *AX 将AX指向的内存区域8byte赋值给BX
MOVQ 16(AX), BX // => BX = *(AX + 16)//不加括号是值的引用
MOVQ AX, BX // => BX = AX 将AX中存储的内容赋值给BX
跳转:
// 无条件跳转
JMP addr // 跳转到地址,地址可为代码中的地址
JMP label // 跳转到标签,可以跳转到同一函数内的标签位置
JMP 2(PC) // 以当前指令为基础,向前/后跳转 x 行// 有条件跳转
JLS addr
地址运算:
// 2代表 scale,scale只能是0、2、4、8
LEAQ (AX)(AX*2), CX // => CX = AX + (AX * 2) = AX * 3
以func neg(x uint64) int64
为例,会生成类似如下的汇编代码:
TEXT ·neg(SB), NOSPLIT, $0-16MOVQ x+0(FP), AXNEGQ AXMOVQ AX, ret+8(FP)RET
以中间点·
作为名的分隔符;没有前缀的·foo
等价于main·foo
$0-16
:其中0表示函数栈帧大小为0(即,没有局部变量);16表示参数及返回值的大小;生成Go汇编:
go tool compile -S -N -l main.go
直接输出汇编;go build -gcflags="-N -l -S" main.go
直接输出汇编;下一篇:SAR回波的多普勒特性