Plan 9汇编
寄存器:
数据寄存器:R0-R7,地址寄存器:A0-A7,浮点寄存器:F0-F7。
伪栈寄存器:FP, SP, TOS。
FP是frame pointer,0(FP)是第一个参数,4(FP)是第二个。
SP是local stack pointer,保存自动变量。0(SP)是第一个。
TOS是top of stack寄存器,用来保存过程的参数,保存局部变量。
汇编器可以有一个变量名,比如p+0(FP),表示p是第一个参数,这个变量保存在符号表内,但是对程序运行没有影响。
数据:
所有的外部引用都需通过伪寄存器: PC(virtual Program Counter)/SB(Static Base register)。
PC用来控制程序执行,SB用来引用全局变量。比如:
把全局数组的地址压栈:MOVL $array(SB), TOS。
把全局数组的第二个元素压栈:MOVL array+4(SB), TOS
local<>+4(SB)是本地变量,只在本文件可见。
定义流程:
TEXT sum(SB), $0
TEXT是一个伪指令,用来定义入口点。后面的参数是函数名,然后是栈大小,通常为0。
中间可以有一个指定loader的选项,设置为1暂停函数的profiling。
设置为2允许一个程序中有多个TEXT符号。
子流程把运算结果放到R0中。浮点的结果放在F0中。
子流程负责保存自己的寄存器,为caller saves模式。
Go语言汇编
Go语言的汇编基于Plan 9的汇编,但是有一些不同。最主要的一个区别是,Go语言的汇编指令不一定直接对应机器表示。有一些直接对应,有一些则不是。
编译器产生的是一些中间码,具体的机器指令是在汇编生成之后才定下来的(Linker的工作)。
FUNCDATA和PCDATA是编译器产生的,用于保存一些给垃圾收集的信息。
Go语言的汇编和Plan 9的另一个不同是操作符的优先级。比如3&1<<2被解释成(3&1)<<2。
符号:
Go语言有4个伪寄存器,实际是对内存位置的一个引用。
FP: 帧指针,保存参数和本地变量
PC:程序指针,负责跳转和流程控制
SB: 静态基指针,全局变量
SP:栈指针,栈顶
所有的符号全部携程FP和SB的偏移的形式:
SB伪寄存器用来表示全局的变量或者函数,不如foo(SB)用来表示foo的地址。加<>表示符号本文件内可见。
FP是用来保存参数的。(0)FP是第一个参数(8)FP是第二个(如果是64位机器)。
SP指向本地栈顶,分别用x-8(SP), y-4(SP)表示变量。
直接的jmp或者call指令,只能指向text符号,不能是符号的偏移。
指令:
TEXT指令定义一个符号,后面紧跟函数体。
DATA指令定义一个section的内存,这段内存并不会被初始化。
DATA symbol+offset(SB)/width, value
GLOBAL指令定义一个符号是全局的
GLOBL divtab<>(SB), RODATA, $64 GLOBL runtime·tlsoffset(SB), NOPTR, $4
divtab是制度的64byte的表格,保存4个byte的整形。tlsoffset是,4byte的no pointers
指令修饰符:
DUPOK:允许一个二进制文件里有多个实例
NOSPLIT: FOR TEXT,routine或者routine的子函数,必须把栈的空间的头填满,用来保护栈分隔
RODATA:FOR DATA/GLOBL,把数据放在只读段
NOPTR: FOR DATA/GLOBL,数据没有指针,不需要被垃圾收集扫描
WRAPPER: FOR TEXT,wrapper function,不需要被以禁用recover计数
NEEDCTXT:FOR TEXT,闭包
Runtime协作:
NOPTR和RODATA的数据不需要被垃圾收集。比指针还要小的数据也被当做NOPTR。不要在go汇编里写非只读数据。
语法:
plan9函数调用协议中采用的是caller-save的模式,也就是由调用者负责保存寄存器。
TEXT !$Add(SB),$0 MOVQ x+0(FP), BX MOVQ y+8(FP), BP ADDQ BP, BX MOVQ BX, ret+16(FP) RET
a+8(FP)
变量名+偏移(寄存器)。FP其实就是BP(栈基址寄存器上移一个机器字长位置的内存地址)。
TEXT ·add(SB),NOSPLIT,$0
00B7分隔了包名和变量名。NOSPLIT表示不用写入参数的大小,$0表示参数的大小,因为制定了NOSPLIT所以写0
汇编文件的名字甚至变量名称并不重要。
MOVQ $0, DX //put 0 into DX register. Q means quadword which is 8 bytes. L for 4 bytes. (src, destination) order
¥24-8表示24字节的帧,和8个字节的参数
Go Array有不是指向第一个元素的指针,而是代表整个数组。赋值或者传递的时候是有一个copy的。
Go slice有三个部分, 一个指针,一个长度,一个容量,指针8个字节,长度和容量4个字节
一个slice没有固定的长度。slice不能指定长度。一个slice如果要增加capacity必须创建一个新的容量更大的slice,并copy内容过去。