8.4 CPU 专题:从 C 程序到机器指令
408 指令系统与 CPU 综合题的解法——C 循环与汇编代码的对应、指令格式与机器码、相对转移目标地址与偏移量(MIPS 与 x86 小端)、指令执行次数,以及流水线中的数据相关分析。
408 怎么考 CPU 与指令
指令系统和 CPU 的综合题通常给出一段 C 代码及其反汇编结果(每行包括指令地址、机器码、汇编指令),然后围绕它提问:
| 常见问法 | 用到的知识 |
|---|---|
| 指令字长多少?某指令是什么格式?操作码、寄存器字段各几位? | 指令格式(第 4 章) |
| 某条转移指令的目标地址 / 偏移量是多少? | 相对寻址,PC 的自增 |
| 某变量存放在哪个寄存器?数组元素的地址如何计算? | 机器级代码表示 |
| 循环共执行多少条指令?某条指令执行多少次? | 程序结构分析 |
| 流水线中哪些指令存在数据相关?要阻塞几个周期? | 指令流水线(第 5 章) |
| 过程调用时栈帧如何变化?返回地址是多少? | 过程调用(第 4 章「程序的机器级代码表示」) |
循环的机器级代码
for 循环编译后通常分为四部分:初始化(只执行一次)、条件判断(每轮一次,外加最后一次判断失败)、循环体和更新、跳回条件判断。以数组求和为例(MIPS,数组 a 的首地址在 $s0,n 在 $s1,sum 在 $s2,i 在 $t0):
| C 代码 | 对应的指令 | 每次执行的条数 |
|---|---|---|
sum = 0; i = 0; | 两条 add | 1 次 × 2 |
i < n | slt + beq | (n + 1) 次 × 2 |
sum += a[i] | sll(i × 4)、add(求 &a[i])、lw、add | n 次 × 4 |
i++ 并回到开始 | addi、j | n 次 × 2 |
总条数 = 2 + 2(n + 1) + 4n + 2n + 1(最后保存 sum 的 sw)= 8n + 5。注意数组下标要乘以元素大小(sll 左移 2 位即 × 4)才能得到字节地址。
下面的动画逐条执行这段程序。右侧显示当前指令的各字段,左侧最后一列统计每条指令的执行次数。修改数组 a 的内容和长度可以验证 8n + 5:
1int sum = 0;2for (int i = 0; i < n; i++)3 sum += a[i];
指令格式与机器码
MIPS 指令定长 32 位,只有三种格式:
| 格式 | 字段(位数) | 用于 |
|---|---|---|
| R 型 | op(6) | rs(5) | rt(5) | rd(5) | shamt(5) | funct(6) | 寄存器之间的运算:add、slt、sll…;op 全为 0,由 funct 区分具体操作 |
| I 型 | op(6) | rs(5) | rt(5) | imm / offset(16) | 立即数运算 addi、访存 lw/sw(基址 + 偏移)、条件转移 beq/bne(相对寻址) |
| J 型 | op(6) | target(26) | 无条件跳转 j:目标 = (PC + 4) 的高 4 位 ‖ target ‖ 00 |
把各字段的二进制按顺序拼接起来,每 4 位写成一个十六进制数字,就是机器码。反过来,给出机器码也可以拆出各字段。
针对上面的程序:(1) 指令 beq $t1, $zero, exit 位于 0040000CH,exit 的地址为 00400028H,beq 的 offset 字段是多少?写出这条指令的机器码(beq 的 op = 4,$t1 为 9 号寄存器)。(2) j loop 的 target 字段是多少?(3) 若 n = 100,这段程序共执行多少条指令?其中 lw 执行多少次?(4) 第 3 行 slt 的机器码为 0111482AH,说出它的格式和各字段的值。
查看解答
(1) 执行 beq 时 PC 已指向下一条指令 00400010H。offset = (00400028H − 00400010H) / 4 = 18H / 4 = 6。机器码字段:op = 000100,rs = 01001($t1),rt = 00000($zero),offset = 0000 0000 0000 0110,拼接得 0001 0001 0010 0000 0000 0000 0000 0110 = 11200006H。
(2) loop 的地址 00400008H,去掉最低 2 位(右移 2 位)并保留低 26 位:target = 0100002H(26 位二进制 00 0001 0000 0000 0000 0000 0010),机器码 08100002H。
(3) 8n + 5 = 805 条;lw 只在循环体中,执行 100 次。
(4) 0111482AH = 000000 01000 10001 01001 00000 101010:op = 0,是 R 型;rs = 8($t0),rt = 17($s1),rd = 9($t1),shamt = 0,funct = 2AH(slt)。
相对转移的目标地址
相对寻址的基准是取指之后的 PC,也就是下一条指令的地址。不同指令系统的约定不同:
| 指令系统 | (PC) 的值 | 偏移量的单位 | 目标地址 |
|---|---|---|---|
| MIPS(定长 4 B) | 本指令地址 + 4 | 字(4 B) | (PC) + offset × 4 |
| x86(变长) | 本指令地址 + 本指令长度 | 字节 | (PC) + 偏移量 |
| 按字编址、单字长指令 | 本指令地址 + 1 | 字 | (PC) + 偏移量 |
x86 是小端机器:多字节的偏移量在机器码中低位字节在前。例如 call 的机器码 E8 D1 FF FF FF 中,E8 是操作码,偏移量是 FFFFFFD1H = −47。
- 机器码中的偏移字节(按地址从低到高):D1 FF FF FF
- 小端方式:低地址存低位字节,因此偏移量 = FFFFFFD1H,按 32 位补码解释为 -47
- 取指后 PC 已指向下一条指令:(PC) = 0040102AH + 5 = 0040102FH
- 目标地址 = (PC) + 偏移量 = 0040102FH − 47(2FH)= 00401000H
某 x86 程序的反汇编片段如下(按字节编址):
0040102A E8 D1 FF FF FF call f1
0040102F ...
00401084 7E 0F jle ...
00401086 ...(1) call 指令调用的函数 f1 的首地址是多少?(2) jle 指令转移成功时的目标地址是多少?(3) 若在 00401095H 处用 2 字节的 jmp(操作码 EBH)跳回 00401060H,它的机器码是什么?
查看解答
(1) call 指令长 5 字节,(PC) = 0040102AH + 5 = 0040102FH;偏移量按小端读作 FFFFFFD1H = −47 = −2FH。f1 的首地址 = 0040102FH − 2FH = 00401000H。call 同时把返回地址 0040102FH 压栈。
(2) jle 长 2 字节,(PC) = 00401086H,偏移量 0FH = 15,目标 = 00401086H + 0FH = 00401095H。
(3) (PC) = 00401095H + 2 = 00401097H,偏移量 = 00401060H − 00401097H = −37H = −55,8 位补码为 C9H,机器码为 EB C9。
流水线中的数据相关
把上面的循环放到 IF、ID、EX、MEM、WB 五段流水线中执行,相邻的指令经常**写后读(RAW)**同一个寄存器。设寄存器堆”前半周期写、后半周期读”,则:
- 不采用旁路时,结果在 WB 段写回。后一条指令与前一条相邻(距离 1)需阻塞 2 个周期,距离 2 需阻塞 1 个周期,距离 ≥ 3 不必阻塞;
- **采用旁路(转发)**时,ALU 的结果可以直接送给下一条指令的 EX 段,不必阻塞;只有 load-use(
lw后紧跟使用其结果的指令)仍需阻塞 1 个周期。
不考虑转移指令引起的控制冒险,分析循环体每轮执行时的数据相关:哪些指令对之间存在 RAW 相关?不采用旁路和采用旁路时,每轮各需要阻塞多少个周期?
查看解答
| 写寄存器的指令 | 读该寄存器的指令 | 寄存器 | 距离 | 无旁路 | 有旁路 |
|---|---|---|---|---|---|
slt $t1, … | beq $t1, … | $t1 | 1 | 2 | 0 |
sll $t2, … | add $t2, $t2, $s0 | $t2 | 1 | 2 | 0 |
add $t2, … | lw $t3, 0($t2) | $t2 | 1 | 2 | 0 |
lw $t3, … | add $s2, $s2, $t3 | $t3 | 1 | 2 | 1(load-use) |
addi $t0, … | 下一轮的 slt $t1, $t0, … | $t0 | 2(中间隔着 j) | 1 | 0 |
不采用旁路时每轮阻塞 2 + 2 + 2 + 2 + 1 = 9 个周期;采用旁路后只剩 load-use 的 1 个周期。编译器还可以通过调整指令顺序消除它,例如把 addi $t0, $t0, 1 移到 lw 与使用 $t3 的 add 之间。
用第 5 章「指令流水线」中的动画可以观察阻塞和旁路的效果。
即时练习
- 1MIPS 指令 beq 位于地址 0040 0100H,其 16 位 offset 字段为 FFFCH,则转移成功时的目标地址为( )。
- 2某 x86 程序中,地址 0040 1010H 处的指令为 2 字节的 jmp,机器码为 EB F0,则转移目标地址为( )。
- 3对本节的求和程序,若 n = 100,则共执行的指令条数为( )。
- 4在 x86 小端机器上,int 型变量 x = 12345678H 存放在地址 1000H 开始的 4 个字节中,则地址 1000H 和 1003H 处的字节分别为( )。
- 5在采用旁路技术的五段流水线中,下列指令序列中仍然必须阻塞的是( )。
标记完成后,侧栏会显示进度。进度仅保存在本浏览器中。