栈与数据传送指令
在程序的执行过程中,需要在CPU和内存之间进行频繁的数据存取,例如,CPU执行加法c=a+b
,首先通过执行数据传送指令将a
和b
的值从内存读到寄存器内
寄存器就是CPU内的一种数据存储部件,只不过容量比较小,比如,寄存器rax
是64Bit
,即8Byte
,如果变量a
是long
类型,需要占用8个字节,因此,rax
全部的数据位都用来保存变量a
;如果变量a
是int
类型,就只需要低32位来存储,如果是short
类型,则只需要低16位
对于寄存器
rax
,如果使用全部的64位,用%rax
表示,32位用%eax
表示,低16位用%ax
表示,低8位用%al
表示
计算完结果后,再通过数据传送指令将结果保存到内存
代码示例
long exchange(long *xp, long y) {
long x = *xp;
*xp = y;
return x;
}
int main()
{
long a = 4;
long b = exchange(&a, 3);
printf("a = %ld, b = %ld\n", a, b);
return 0;
}
a = 3, b = 4
这个程序会将变量a
的值替换成3,变量b
保存原来a
的值4
exchange
函数的汇编代码,包括两条数据传送指令和一条返回指令
xp -> %rdi, y -> %rsi
exchange:
movq (%rdi), %rax // rdi指向的地址的值复制到rax
movq %rsi, (%rdi) // rsi的值复制到rdi指向的地址处
ret
寄存器rdi
和rsi
分别用来保存函数传递的第一个参数和第二个参数,因此rdi
中保存了xp
的值,rsi
中保存了变量y
的值
第一条mov
指令从内存中读取数值到寄存器,内存地址保存在寄存器rdi
中,目的操作数是寄存器rax
,因为要返回x
的值,所以直接放到rax
里面
第二条mov
指令将变量y
的值写到内存里,变量y
存储在rsi
中,内存地址保存在rdi
中,也就是xp
指向的地址
程序栈
要将寄存器rax
的值0x123
放入栈中,可以使用pushq
指令将数据压入栈中,该指令执行的过程可以分解为两步
pushq %rax // 将rax的值压栈
首先指向栈顶的寄存器rsp
进行减法操作,因为栈是从高地址指向低地址,例如此时rsp
的值为0x108
,rsp值减8之后指向0x100
,然后将需要保存的数据复制到新的栈顶位置,此时地址0x100
处将保存寄存器rax
的数值0x123
subq $8, %rsp // rsp的值减8
movq %rax, (%rsp) // 保存到rsp指向的地址处
区别在于pushq
指令只要1个字节,而两条指令需要8个字节
pushq
的本质是将数据写入到内存
与之对应的pop
指令就是从内存中读取数据,并且修改栈顶指针
popq %rbx
这条指令将栈顶保存的值复制到寄存器rbx
中,也可以分解为两步:
- 首先从栈顶读出数据,复制到寄存器
rbx
- 然后将栈顶指针加8
movq (%rsp), %rbx
addq $8, %rsp
注意,此时地址0x100
内保存的数据0x123
依然存在,知道下次push
操作,才会被覆盖