(学习笔记)3.6 控制(3.6.1 条件码& 3.6.2 访问条件码)
文章目录
- 线索栏
- 笔记栏
-
- 1. 控制流基础
-
- 1)目标
- 2)机器级机制
- 2. 条件码寄存器
-
- 设置规则
- 3. 比较与测试指令 (CMP & TEST)
- 4. 访问条件码:SET指令
-
- 1)指令格式
- 2)关键后缀规律
- 3)同义名
- 4)SET指令表(图3-14)要点
- 5. 代码示例:comp函数
-
- 关键步骤
- 6. 练习题
-
- 练习题3.13
- 练习题3.14
- 总结栏
线索栏
- 核心机制:机器代码如何实现C语言中的条件控制(如if、while)?
- 条件码:CPU维护了哪几个单比特的条件码寄存器?ADD指令执行后,它们如何被设置?(以t = a + b为例)
- 专用指令:除了算术/逻辑指令,哪两条指令只设置条件码而不改变目的寄存器?它们分别基于什么操作?
- SET指令:访问条件码的第一种方式是什么?SET指令的目的操作数是什么大小?
- 命名与规律:如何从SET指令的后缀(如l, b, e, ne)判断它用于有符号比较还是无符号比较?setg和 setnle是什么关系?
- 代码生成:编译器如何生成指令序列来计算表达式 a < b?(以comp函数为例,结合CMP、SET、MOVZ指令
- 类型推导:如何根据给定的CMP/TEST和SET指令序列,反向推断出源代码中数据的类型(int?unsigned long?)和比较操作(<?>=?)?(练习题3.13, 3.14)
笔记栏
1. 控制流基础
1)目标
实现C语言的条件语句、循环等。
2)机器级机制
(1)测试数据:通过运算设置条件码(Condition Codes)。
(2)改变控制流:根据条件码,通过JUMP指令跳转到代码的不同部分。
2. 条件码寄存器
CPU维护一组单比特寄存器,描述最近一次算术/逻辑运算的属性:
| 条件码 | 名称 | 触发条件 (以 t = a + b为例) | 解释 |
|---|---|---|---|
| CF | 进位标志 | (unsigned)t < (unsigned)a | 无符号溢出(最高位进位) |
| ZF | 零标志 | (t == 0) | 结果为0 |
| SF | 符号标志 | (t < 0) | 结果为负数(符号位为1) |
| OF | 溢出标志 | (a<0 == b<0) && (t<0 != a<0) | 有符号溢出 |
设置规则
(1)LEA指令不改变条件码。
(2)图3-10中大多数指令(ADD, SUB, INC, DEC, 位运算等)都会设置条件码。
(3)移位操作:CF= 最后移出的位;OF=0。
3. 比较与测试指令 (CMP & TEST)
这两条指令只设置条件码,不改变目的寄存器。

| 指令类 | 行为 (ATT格式) | 描述 |
|---|---|---|
| CMP S1, S2 | 计算 S2 – S1, 据此设置条件码 | 比较。操作数顺序与SUB相反 (CMP b, a比较 a和 b)。 |
| TEST S1, S2 | 计算 S1 & S2, 据此设置条件码 | 测试(通常用于检查位或是否为0)。 |
4. 访问条件码:SET指令
SET指令根据条件码的某种组合,将一个字节(Byte) 设置为0或1,这是访问条件码的第一种方式。
1)指令格式
setX D, 其中X是表示条件组合的后缀,D是单字节寄存器(如%al)或内存地址。
2)关键后缀规律
(1)用于有符号比较:g(greater), l(less), e(equal)等。
(2)用于无符号比较:a(above), b(below), e(equal)等。
3)同义名
同一条机器指令可能有多个名字(如setg≡ setnle)。
4)SET指令表(图3-14)要点

5. 代码示例:comp函数
int comp(data_t a, data_t b) { return a < b; }
// a in %rdi, b in %rsi
comp:
cmpq %rsi, %rdi # 比较 a 和 b (计算 a - b)
setl %al # 若 a < b (有符号),则 %al = 1,否则为0
movzbl %al, %eax # 零扩展%al到%eax (同时清零高4字节)
ret
关键步骤
CMP→ SET→ MOVZ扩展。
6. 练习题
练习题3.13

根据指令序列推断data_t和COMP。
A. cmpl %esi, %di; setl %al
cmpl:操作双字(4字节)→ data_t= int。
setl:有符号小于 → COMP= <。
B. cmpw %si, %di; setge %al
cmpw:操作字(2字节)→ data_t= short。
setge:有符号大于等于 → COMP= >=。
C. cmpb %sil, %dil; setbe %al
cmpb:操作字节(1字节)→ data_t= char。
setbe:无符号低于等于 → COMP= <=(无符号)。
D. cmpq %rsi, %rdi; setne %al
cmpq:操作四字(8字节)→ data_t= long或 unsigned long或指针。
setne:不等于(不区分符号)→ COMP= !=。

练习题3.14

根据TEST和SET推断data_t和TEST。TEST a, a用于测试a本身。
A. testq %rdi, %rdi; setge %al
testq:测试8字节 → data_t= long。
setge:有符号非负 → TEST= >=0。
B. testw %di, %di; sete %al
testw:测试2字节 → data_t= short。
sete:等于0 → TEST= ==0。
C. testb %dil, %dil; seta %al
testb:测试1字节 → data_t= char或 unsigned char。
seta:无符号大于 → 在TEST后,a>0等价于 a != 0→ TEST= !=0(但更准确是>0,对无符号数两者等价)。
D. testl %edi, %edi; setne %al
testl:测试4字节 → data_t= int。
setne:不等于0 → TEST= !=0。

总结栏
本节是理解机器级控制流的基石,引入了条件码系统及如何通过SET指令将其转换为布尔值。
- 条件码是状态记录器:CF, ZF, SF,OF四个标志位记录了上一次算术/逻辑运算的关键副作用(溢出、零、符号)。CMP和TEST是专门为比较测试而设、不修改操作数的指令。
- SET指令是转换器:它将条件码的复杂组合(如图3-14中的SF^OF)转换为一个简单的布尔值(0或1),存入一个字节。这是实现C语言关系表达式(如a<b)返回值的关键步骤。
- 机器不区分类型,但指令选择体现语义:CPU执行相同的CMP指令,但根据后续SET指令的不同后缀(lvsb)来决定进行有符号还是无符号比较。这是理解汇编代码中数据类型信息的关键。
- 规律性:SET指令的后缀命名具有规律:l/g系列用于有符号,a/b系列用于无符号。e/ne通用。条件组合基于CMP a,b(计算a-b)的结果。
- 逆向工程训练:练习题3.13和3.14强化了从指令序列到高级语言结构的反向推理能力。需综合指令后缀(b/w/l/q)→数据类型大小、SET后缀(l/b/a/g…)→ 比较类型和符号性两条线索。
核心启示:计算机通过“先计算状态,后查询状态做出决策”来实现条件分支。条件码系统是这一“状态”的标准化、硬件化的表示。SET指令是连接底层状态与高层逻辑判断的桥梁。掌握条件码和SET指令,是理解后续JUMP(条件跳转)和CMOV(条件传送)指令的基础。