offset leftshift
Q1:
“计算branch分支目标地址时,需要将偏移量左移1位以表示半字为单位的偏移量,使偏移量的有效范围扩大到2倍。所以偏移量在经过ImmGen的符号扩展后,还要被Shift left左移1位,再进入Add加法器与PC相加”这个左移1位到底是为了什么?举个例子详细的说明
A1 :
在 MIPS 或类似架构中,分支指令的目标地址计算中,偏移量的左移 1 位是因为目标地址需要按照 字节地址对齐,而分支指令中的偏移量通常以 字对齐(halfword, 2 字节) 的形式编码。
具体来说,分支指令的偏移量字段表示的是相对于当前指令地址的 半字(2 字节)单位的偏移量,但程序计数器(PC)操作时以字节为单位。因此,在计算分支目标地址时,需要将偏移量左移 1 位(乘以 2),以将半字单位转换为字节单位。
详细分析:
分支指令中的偏移量
- 假设偏移量字段为
imm
,它在指令中以半字单位表示。 - 如果直接将
imm
与PC
相加,则计算出的地址是以半字为单位的,但实际地址是以字节为单位的。
- 假设偏移量字段为
符号扩展
- 如果
imm
是一个 16 位数值,可能是有符号的,所以需要符号扩展为 32 位(或者符合当前架构的宽度)。
- 如果
左移 1 位
- 将符号扩展后的
imm
左移 1 位,相当于乘以 2,从而将半字单位转换为字节单位。
- 将符号扩展后的
加法器计算目标地址
- 最终的目标地址通过公式
目标地址 = PC + 偏移量
计算,其中PC
是当前指令的地址,而偏移量已经是字节单位。
- 最终的目标地址通过公式
举例说明:
假设当前指令的地址是 0x00400010
,分支指令的偏移量字段 imm
为 0x0003
(十进制 3)。分支目标地址的计算过程如下:
符号扩展:
imm
是 16 位的数值(0x0003
),扩展为 32 位:复制代码
符号扩展后:0x00000003
左移 1 位:
将扩展后的imm
左移 1 位,相当于乘以 2:bash
复制代码
左移 1 位:0x00000003 << 1 = 0x00000006
与
PC
相加:
将左移后的偏移量与当前指令地址(PC = 0x00400010
)相加,计算目标地址:bash
复制代码
目标地址 = PC + (imm << 1) = 0x00400010 + 0x00000006 = 0x00400016
为什么需要左移 1 位?
分支偏移量的设计目的是节省指令字段的长度。
- 如果不按半字对齐,偏移量需要精确到字节单位,这样指令中需要更多的位来表示偏移范围。
- 通过规定分支目标地址总是半字对齐,可以节省 1 位的偏移字段表示,同时保证跳转目标地址是合法对齐的。
因此,通过左移 1 位,将偏移量恢复为字节单位,满足实际计算需求的同时也减少了指令编码的位数开销。
总结:
偏移量左移 1 位是为了将 以半字为单位的偏移量 转换为 以字节为单位的偏移量,从而与 PC
的单位一致,确保目标地址的正确计算。
Q2:
但这样一来便宜的量不就变成本来应该偏移的量的2倍了吗?那不就跳转到非目的地的地址了吗
A2:
这是一个很好的问题!实际上,偏移量左移 1 位并不会导致跳转到错误的地址,因为指令的偏移量本身就已经按照 半字单位 编码,因此在目标地址计算时,左移 1 位只是将这个半字单位还原为字节单位,使它能够与地址计算的单位(字节)一致。
让我们通过更细致的例子来说明这个问题,以及为什么不会跳到错误地址。
指令编码的背景
在 MIPS 架构等体系中,指令地址是 字节对齐的,即每条指令的地址是 4 的倍数(对 32 位字长机器)。
但分支指令的 偏移量字段 是以 半字为单位(2 字节) 表示,这是一种节省位数的设计,因为:
- 目标地址通常是相对的,所以用相对偏移量即可。
- 偏移量字段的单位选择为半字,已经隐含了「目标地址总是对齐到 2 字节」这一假设。
偏移量设计如何保证正确性?
因为分支指令的偏移量以半字单位编码,而地址计算需要字节单位:
- 如果偏移量没有左移 1 位,计算出来的地址就会错。
- 左移 1 位后,偏移量的单位从半字变成了字节,与实际的程序计数器(PC)的单位一致,计算出的目标地址是正确的。
详细举例分析
假设以下场景:
- 当前指令地址
PC = 0x00400010
。 - 分支指令的偏移量字段
imm = 3
,表示「向前跳过 3 个半字」。
1. 假设没有左移 1 位的情况下计算
目标地址直接按 PC + imm
来算(错的):
markdown
复制代码
目标地址 = 0x00400010 + 3 = 0x00400013 (这个地址不是对齐的地址,也不是正确目标地址)
错误原因:
偏移量 imm
是以半字单位编码,而 PC
是字节单位,这里的偏移量应乘以 2,才能表示正确的字节单位。
2. 正确计算(偏移量左移 1 位)
正确计算时,偏移量 imm
左移 1 位(乘以 2),恢复为字节单位:
bash
复制代码
左移 1 位后偏移量:imm << 1 = 3 << 1 = 6 (字节单位) 目标地址 = PC + (imm << 1) = 0x00400010 + 6 = 0x00400016 (这是正确的目标地址)
结果对比
- 没有左移 1 位: 目标地址错了,可能会跳到一个奇怪的、不对齐的地址。
- 左移 1 位: 目标地址正确。
进一步理解:为什么偏移量不等于 2 倍?
一个重要的点是 偏移量的实际含义:
指令中的偏移量字段(
imm
)是一个 相对地址的缩略表示,编码时选择了半字为单位。- 偏移量
3
表示「跳过 3 个半字」,但程序计数器和内存地址以字节为单位工作,所以在计算中必须将3
转换为字节单位,即3 × 2 = 6
字节。
- 偏移量
偏移量左移 1 位后,并不是修改了偏移量的实际大小,而是 调整了表示单位,从半字单位变成了字节单位。
总结
- 左移 1 位不会导致跳到非目的地址,因为偏移量的原始编码就是以半字单位设计的。
- 左移 1 位只是为了让偏移量与地址的单位一致,确保计算结果正确。
- 如果不左移 1 位,反而会导致跳转错误,因为单位不匹配。