Appearance
题目
某程序中有如下循环代码段 P:“for (int i=0; i<N; i++) sum += A[i];”。假设编译时变量 sum 和 i 分别分配在寄存器 R1 和 R2 中。常量 N 在寄存器 R6 中,数组 A 的首地址在寄存器 R3 中。程序段 P 起始地址为 08048100H,对应的汇编代码和机器代码如下表所示。
| 编号 | 地址 | 机器代码 | 汇编代码 | 注释 |
|---|---|---|---|---|
| 1 | 08048100H | 00022080H | loop: sll R4, R2, 2 | (R2)<<2 → R4 |
| 2 | 08048104H | 00083020H | add R4, R4, R3 | (R4) + (R3) → R4 |
| 3 | 08048108H | 8C850000H | load R5, 0(R4) | ((R4) + 0) → R5 |
| 4 | 0804810CH | 00250820H | add R1, R1, R5 | (R1) + (R5) → R1 |
| 5 | 08048110H | 20420001H | add R2, R2, 1 | (R2) + 1 → R2 |
| 6 | 08048114H | 1446FFFAH | bne R2, R6, loop | if (R2)≠(R6) goto loop |
执行上述代码的计算机 M 采用 32 位定长指令字,其中分支指令 bne 采用如下格式:
Op 为操作码,Rs 和 Rd 为寄存器编号,OFFSET 为偏移量,用补码表示。请回答下列问题,并说明理由。
(1) M 的存储器编址单位是什么?
(2) 已知 sll 指令实现左移功能,数组 A 中每个元素占多少位?
(3) 题 44 表中 bne 指令的 OFFSET 字段的值是多少?已知 bne 指令采用相对寻址方式,当前 PC 内容为 bne 指令地址,通过分析题 44 表中指令地址和 bne 指令内容,推断出 bne 指令的转移目标地址计算公式。
(4) 若 M 采用如下“按序发射、按序完成”的 5 级指令流水线:IF(取指)、ID(译码及取数)、EXE(执行)、MEM(访存)、WB(写回寄存器),且硬件不采取任何转发措施,分支指令的执行均引起 3 个时钟周期阻塞,则 P 中那些指令的执行会由于数据相关而发生流水线阻塞?哪条指令的执行会发生控制冒险?为什么指令 1 的执行不会因为与指令 5 的数据相关而发生阻塞?
解析
本题把 指令格式 + 编址单位 + 相对寻址 + 流水线冒险 4 块知识串到一个 MIPS 风格的循环上。整体思路:
- 由相邻指令地址差倒推编址单位(与 CPI 无关);
- sll 指令的左移位数告诉我们数组元素的字宽;
- bne 是相对寻址,要把 PC 自增、OFFSET 符号扩展、再乘 4 算到字节单位上;
- 数据冒险逐条排查,分支冒险是结构性的(fetch 时还不知道目标)。
(1) 存储器编址单位 [2 分]
计算机 M 是 32 位定长指令字,每条指令 4B。题表中相邻指令地址差:
4 个地址单位刚好对应 4B → 每个地址单位 = 1B → 按字节编址。
(2) 数组 A 中每个元素的位数 [2 分]
指令 1 是 sll R4, R2, 2,把循环变量 i(在 R2)左移 2 位 → 乘以 4 → 写入 R4 作为偏移;指令 2 把这个偏移加上数组首地址 R3,得到 &A[i]。
也就是说:"i 增 1 → 偏移地址增 4 字节"——数组每个元素占 4 字节。配合按字节编址:
(3) bne 的 OFFSET 字段与目标地址公式 [3 分]
Step 1. 提取 OFFSET。
bne 机器码 = 1446 FFFAH = 0001 0100 0100 0110 1111 1111 1111 1010B。指令格式中低 16 位是 OFFSET:
按 16 位补码解读:FFFAH 取反 +1 = 0006H → OFFSET = −6。
Step 2. 反推目标地址公式。
bne 自身在 08048114H,其要跳到 08048100H(loop 标签)。fetch 完 bne 后 PC 自动 + 4:
目标与 PC(取指后)之差:
OFFSET = −6 而真实字节位移 = −24,比例 4 倍 → OFFSET 以"指令字"为单位,要 ×4 才是字节单位:
编者注(生僻术语): 这是 MIPS 风格的"PC-相对、字对齐"寻址。指令 4B 对齐意味着字节位移的最低 2 位永远是 0,索性在指令编码层面省掉 → OFFSET 用作"指令数偏移",硬件再左移 2 位(×4)。代价是 OFFSET 寻址范围 ×4,扩展到 ±128KB。
(4) 流水线阻塞分析 [4 分]
逐条排查数据相关:
| 指令 | 读寄存器 | 写寄存器 | 数据相关源 | 是否阻塞 |
|---|---|---|---|---|
| 1 sll R4, R2, 2 | R2 | R4 | — | 否 |
| 2 add R4, R4, R3 | R4, R3 | R4 | R4 来自 I1 | ✅ 阻塞 |
| 3 load R5, 0(R4) | R4 | R5 | R4 来自 I2 | ✅ 阻塞 |
| 4 add R1, R1, R5 | R1, R5 | R1 | R5 来自 I3 | ✅ 阻塞 |
| 5 add R2, R2, 1 | R2 | R2 | — | 否 |
| 6 bne R2, R6 | R2, R6 | — | R2 来自 I5 | ✅ 阻塞 |
故数据相关阻塞发生在第 2、3、4、6 条指令。
控制冒险:
第 6 条 bne 是分支指令,在 IF 之后需要等到执行结果(决定跳还是不跳)才能确定下一条指令地址 → 第 6 条引起 3 个周期的控制冒险。
为什么 I1(下一轮)不会因为 I5 阻塞?
跨循环考虑:上一轮的 I5 写 R2,下一轮的 I1 读 R2,看似数据相关。但夹在它们之间的 I6(bne)已经引起了 3 个周期的控制冒险阻塞——这 3 个周期足够让 I5 的 WB 完成,等下一轮 I1 进入 ID 时 R2 早已写完。
易错点: 别混淆"数据冒险阻塞数"和"控制冒险阻塞数"。本题说"分支指令引起 3 个周期阻塞",这是控制冒险(因为不知道下一条指令地址),与数据冒险(无转发时通常 3 周期)刚好匹配——所以本题里"控制冒险" oneself 同时也覆盖掉了 I5 → I1 的潜在数据冒险。
【评分说明】若考生回答"因为指令 1 和 2、2 和 3、3 和 4、5 和 6 发生数据相关,发生阻塞的指令为第 2、3、4、6 条",同样给 3 分。答对 3 个以上给 3 分,部分正确酌情给分。