3.6 控制(control)
jump
指令可以改变一组机器代码指令的执行顺序。
3.6.1 条件码(condition code)
条件码寄存器。
CF(carry flag): 进位标志。最近的操作使最高位产生了进位。可用来检查无符号操作的溢出。
ZF(zero flag):零标志。最近的操作得出的结果为零。
SF(signed flag):符号标志。最近的操作得到的结果为负数。
OF(overflow flag):溢出标志。最近的操作导致一个补码溢出——正溢出或负溢出。
标志(flag ) |
执行 | 效果 |
---|---|---|
CF |
(unsigned) t < (unsigned) a |
无符号溢出 |
ZF |
t == 0 |
零 |
SF |
t < 0 |
负数 |
OF |
a < 0 == b < 0 && (t < 0 != a<0 |
有符号溢出 |
leaq
指令不会改变任何条件码,因为它是用来进行地址计算的。
3.6.2 访问条件码
条件码通常不会直接读取,常用的使用方法有三种:
1) 可以根据条件码的某种组合,将一个字节设置为 0 或者 1;
2)可以条件跳转到程序的某个其他的部分;
3)可以有条件地传送数据。
3.6.3 跳转指令
3.6.4 跳转指令的编码
3.6.5 用条件控制来实现条件分析
将条件表达式和语句从C语言翻译成机器代码,最常用的方式就结合有条件和无条件跳转。
C语言中的 if-else 语句的通用形式模版如下:
if (test-expr)
then-statement
else
else-statement
对应汇编实现通常会使用下面这种形式。以C语言语法来描述控制流:
t = test-expr;
if (!t)
goto false;
then-statement;
goto done;
false:
else-statement;
done:
3.6.6 用条件传送来实现条件分支
实现条件操作的传统方法是通过使用控制的条件转移(条件满足,执行A;否则执行B)。
一种替代的策略是使用数据的条件转移(在一定条件下,事先把条件结果列出来,根据条件直接选取数据结果来执行下一路径)。
示例(ret
返回的参数存放在 %rax
中):
分支预测错误的处罚
条件传送指令
如果条件分支中任意一个可能产生错误条件或者副作用,将会导致非法的行为。
3.6.7 循环
1. do-while 循环
2. while 循环
第一种翻译方法(jump to middle
)
示例:
第二种翻译方法(guarded-do
)
示例:
有趣的特性。循环测试(汇编代码的第9
行)从原始C
代码的 n>1
变成了 n != 1
。编译器知道只有当 n > 1
时才进入循环,所以将 n
减1
意味着n > 1
或者n = 1
。因此,测试n != 1
就等价于测试n >= 1
。(本人未验证)
3. for 循环
for 循环的通用格式:
for (init-expr; test-expr; update-expr)
body-statement
等价于:
init-expr:
while(test-expr){
body-statement
update-expr;
}
跳转到中间的策略 goto 代码:
init-expr;
goto test;
loop:
body-statement;
update-expr;
test:
t = test-expt;
if(t)
goto loop;
而 guarded-do 策略得到:
init-expr;
t = test-expr;
if(!t)
goto done;
loop:
body-statement;
t = test-expt;
if(t)
goto loop;
done:
综上所述,C语言中三种形式的所有循环——do-while
、while
、和for
都可以用一种简单的策略来翻译,产生包含一个或多个条件分支的代码。控制的条件转移提供将循环翻译成机器代码的基本机制。
3.6.8 switch 语句
switch
(开关)语句可以根据一个整数索引值进行多重分支(multiway branching
)。
跳转表是一个数组,表项 i
是一个代码段的地址,这个代码段实现当开关索引值等于 i
时程序应该采取的动作。
示例:
跳转表