操作数:CP121
立即数(Imm):
- 以$开头
- 立即数寻址
寄存器值(Reg):
- 16位寄存器的低1字节,2字节,4字节或8字节。
- ra表示寄存器a,R[ra]表示寄存器的值
- 寄存器寻址
- 内存引用(Mem)
- 根据计算出的地址来访问某个内存位置
- Mb[Addr]表示存储在以地址开始b个字节的引用
MOV类指令
- 将数据从一个位置复制到另一个位置
- 第1个是源操作数,第2个是目的操作数
- 2个操作数不能都指向内存
示例
long exchange(long *xp,long y)
{
long x=*xp;
*xp=y;
return x;
}
//开始时xp存放在%rdi(存放目标索引值)
//y存放在%rsi(存放源索引值)
exchange:
//寄存器间接寻址
movq (%rdi),%rax
//由于xp是指针,所以%rdi存放的是地址。将位于该内存地址的值复制到%rax(局部变量通常存在寄存器中)
movq %rsi,(%rdi)
//将y的值复制到%rax的内存地址
ret
//返回
CP129
加载有效地址指令:
将源操作数的地址写入目的操作数(必须是寄存器)
long t=x+4y;
//假设x存放在%rdi,y存放在%rsi
leaq (%rdi,%rsi,4),%rax
//将%rax的值设为x+4y
一元操作:操作数可以是寄存器/内存地址
二元操作:
- 第一操作数:立即数/寄存器/内存地址
- 第二操作数:寄存器/内存地址(读出后写回)
比较指令:CP136
CMP S1,S2//基于S2-S1
示例:
long arith(long x,long y)
{
long t1=x^y;
long t2=x*48
return t2;
}
xorq %rsi,%rdi
//结果存在%edi(第一个参数)
leaq (%rsi,%rsi,2),%rax
//3*z
salq $4,%rax
//左移4位,每移1次相当于乘2
跳转指令
无条件跳转:
//直接跳转
jmp .L1//跳转到L1
//间接跳转
jmp *(%rax)//%rax存储的是
条件分支:
- 条件控制:CPU 依靠流水线工作的,比方说执行一系列操作需要 ABCDE 五个步骤。执行 B 所需的数据会在执行 A 的同时加载到寄存器中,如果程序一直是顺序的,效率会很高。
一旦遇到分支,执行完 A 下一步要执行的是 C,但是载入的数据是 B,这时候就要把流水线清空,然后重新载入 C 所需要的数据。『分支预测』这一技术来解决(分支预测有概率可能猜错)。 - 条件传送:
- 先计算出表达式的值
- 只有当表达式的值容易计算时才会采用条件传送
long absdiff(long x, long y)
{
long result;
if (x > y)
result = x-y;
else
result = y-x;
return result;
}
反汇编
long result;
int ntest = x <= y;
if (ntest) goto Else;
result = x-y;
goto Done;
Else:
result = y-x;
Done:
return result;
汇编
movq %rsi, %rax # x
subq %rdi, %rax # result = y-x
movq %rdi, %rdx
subq %rsi, %rdx # eval = y-x
cmpq %rsi, %rdi # x:y
cmovge %rdx, %rax # %rax存储返回值
ret
循环:
- 组合条件测试和跳转产生循环的效果
- do-while
loop:
循环体;
t=循环条件;
if(t) goto loop;
例如
long pcount_goto(long n)
{
long result = 1;
loop:
result *=n;
n=n-1;
if (n>1) goto loop;
return result;
}
pcount_goto:
movl $1, %eax # result = 1
.L2: # loop:
imulq %rdi, %rax # %rdi=n,%rax=result
subq $1, %rdi
cmpq $1, %rdi
jg .L2 # 大于跳转
rep; ret
- while
guarded-do(较高优化等级):初始条件成立时,进入do-while循环( Do-While 语句执行起来更快,更符合 CPU 的运算模型)
t=循环条件;
if(!t)
goto done;
loop:
循环体;
t=循环条件;
if(t)
goto loop;
done:
- for循环
初始表达式;
for (Init; Test; Update)
Body
Init;
while (Test) {
Body
Update;
}
switch
跳转表
- 是1个数组
- 数组元素为代码段,开关索引值等于某个i时进行跳转
- 开关数量比较多且值的跨度比较小时使用
CP160 void* jt[7]是跳转表,元素(例如&&loc_A)代表代码段的地址。为了使下标从尽可能小的地方开始,将n-100作为开关索引值。
过程:
对于每个过程调用来说,都会在栈中分配一个帧 Frames。每一帧里需要包含:返回信息,本地存储(如果需要)临时空间
- 传递控制:调用时将PC设置成被调用函数的起始地址;返回时将PC设置成原函数的下1条指令。
传递数据:
- 参数:不超过6个通过寄存器传递(图3-28)
;超过6个通过栈传递
- 返回值:通过寄存器%rax传递
- 参数:不超过6个通过寄存器传递(图3-28)
分配释放内存:CP170局部变量有时需要存放在内存中
long mult2(long a, long b)
{
long s = a * b;
return s;
}
//汇编
0000000000400550 <mult2>:
# a 在 %rdi 中,b 在 %rsi 中
mov %rdi, %rax # 得到 a 的值
imul %rsi, %rax # a * b
# s 在 %rax 中
retq # 返回
void multstore (long x, long, y, long *dest)
{
long t = mult2(x, y);
*dest = t;
}
0000000000400540 <multstore>:
# x 在 %rdi 中,y 在 %rsi 中,dest 在 %rdx 中
push %rbx # CP173被调用者保护寄存器
mov %rdx, %rbx # 保存 dest
# 程序计数器%rip指向mult2的起始地址
callq 400550 <mult2> # 调用 mult2(x, y)
# 返回结果在 %rax 中
mov %rax, (%rbx) # 保存到 dest指向的地址
pop %rbx # 恢复%rbx的原值
retq # 返回