[go]汇编ASM简介

文章目录

    • 汇编(ASM)
      • 寄存器
      • 帧指针FP
    • 常见指令
      • 函数示例
    • 生成汇编

Go汇编代码主要用于优化和与底层系统交互,并不会像其它的经典汇编代码那样独立运行。

汇编(ASM)

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中有四个预定义的符号作为伪寄存器(虚拟寄存器,伪寄存器使用时一般需要一个标识符和偏移量为前缀):

  • FP: 帧指针,参数和局部变量;
  • PC: 程序计数器,跳转和分支;
  • SB: 静态基址指针,用来定义和读取文本区(代码区)、数据区的;
  • SP: 栈指针,栈的顶端;

伪寄存器SB可以看作是内存的起始地址。foo(SB)就是foo在内存中的地址,有两种修饰符<>和+N(整数)

  • foo<>(SB):表示一个私有元素,只有在同一个源文件中才可以访问;
  • +N:表示相对地址加上一个偏移量后得到的地址,如foo+8(SB)就指向foo之后8个字节处的地址;

帧指针FP

Go汇编使用的是caller-save模式,被调用函数的入参参数、返回值都由调用者维护、准备。因此,当需要调用一个函数时,需要先将这些工作准备好,才调用下一个函数,另外这些都需要进行内存对齐,对齐的大小是 sizeof(uintptr)。
[go]汇编ASM简介_第1张图片

伪寄存器FP是一个虚拟帧指针,被用来引用过程参数(由编译器负责维护):

  • 在64位机器上,0(FP)是第一个参数,8(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-16
    MOVQ     x+0(FP), AX
    NEGQ     AX
    MOVQ     AX, ret+8(FP)
    RET

以中间点·作为名的分隔符;没有前缀的·foo等价于main·foo

  • TEXT: 标识位于text段;
  • ·neg: 函数名(包括包名);
  • (SB): 基址指针,代表函数地址;
  • NOSPLIT: 禁止stack拆分(即函数内部不需要判断栈空间是否足够,不会增长函数栈);编译器必须确保运行函数是安全的,即有足够的栈空间;
  • $0-16:其中0表示函数栈帧大小为0(即,没有局部变量);16表示参数及返回值的大小;

生成汇编

生成Go汇编:

  • go tool compile -S -N -l main.go直接输出汇编;
  • go build -gcflags="-N -l -S" main.go直接输出汇编;
    注意:加上对应的flag,避免某些逻辑被编译器优化掉,而看不到对应完整的汇编代码:
    • -l:禁止内联
    • -N:禁止优化
    • -S:输出汇编代码

你可能感兴趣的:(Go,golang,汇编,ASM,寄存器,FP帧指针)