Post

offset leftshift

offset leftshift

Q1:

“计算branch分支目标地址时,需要将偏移量左移1位以表示半字为单位的偏移量,使偏移量的有效范围扩大到2倍。所以偏移量在经过ImmGen的符号扩展后,还要被Shift left左移1位,再进入Add加法器与PC相加”这个左移1位到底是为了什么?举个例子详细的说明

A1 :

在 MIPS 或类似架构中,分支指令的目标地址计算中,偏移量的左移 1 位是因为目标地址需要按照 字节地址对齐,而分支指令中的偏移量通常以 字对齐(halfword, 2 字节) 的形式编码。

具体来说,分支指令的偏移量字段表示的是相对于当前指令地址的 半字(2 字节)单位的偏移量,但程序计数器(PC)操作时以字节为单位。因此,在计算分支目标地址时,需要将偏移量左移 1 位(乘以 2),以将半字单位转换为字节单位。

详细分析:

  1. 分支指令中的偏移量

    • 假设偏移量字段为 imm,它在指令中以半字单位表示。
    • 如果直接将 immPC 相加,则计算出的地址是以半字为单位的,但实际地址是以字节为单位的。
  2. 符号扩展

    • 如果 imm 是一个 16 位数值,可能是有符号的,所以需要符号扩展为 32 位(或者符合当前架构的宽度)。
  3. 左移 1 位

    • 将符号扩展后的 imm 左移 1 位,相当于乘以 2,从而将半字单位转换为字节单位。
  4. 加法器计算目标地址

    • 最终的目标地址通过公式 目标地址 = PC + 偏移量 计算,其中 PC 是当前指令的地址,而偏移量已经是字节单位。

举例说明:

假设当前指令的地址是 0x00400010,分支指令的偏移量字段 imm0x0003(十进制 3)。分支目标地址的计算过程如下:

  1. 符号扩展
    imm 是 16 位的数值(0x0003),扩展为 32 位:

    复制代码

    符号扩展后:0x00000003

  2. 左移 1 位
    将扩展后的 imm 左移 1 位,相当于乘以 2:

    bash

    复制代码

    左移 1 位:0x00000003 << 1 = 0x00000006

  3. PC 相加
    将左移后的偏移量与当前指令地址(PC = 0x00400010)相加,计算目标地址:

    bash

    复制代码

    目标地址 = PC + (imm << 1) = 0x00400010 + 0x00000006 = 0x00400016

为什么需要左移 1 位?

分支偏移量的设计目的是节省指令字段的长度。

  • 如果不按半字对齐,偏移量需要精确到字节单位,这样指令中需要更多的位来表示偏移范围。
  • 通过规定分支目标地址总是半字对齐,可以节省 1 位的偏移字段表示,同时保证跳转目标地址是合法对齐的。

因此,通过左移 1 位,将偏移量恢复为字节单位,满足实际计算需求的同时也减少了指令编码的位数开销。

总结:

偏移量左移 1 位是为了将 以半字为单位的偏移量 转换为 以字节为单位的偏移量,从而与 PC 的单位一致,确保目标地址的正确计算。

Q2:

但这样一来便宜的量不就变成本来应该偏移的量的2倍了吗?那不就跳转到非目的地的地址了吗

A2:

这是一个很好的问题!实际上,偏移量左移 1 位并不会导致跳转到错误的地址,因为指令的偏移量本身就已经按照 半字单位 编码,因此在目标地址计算时,左移 1 位只是将这个半字单位还原为字节单位,使它能够与地址计算的单位(字节)一致。

让我们通过更细致的例子来说明这个问题,以及为什么不会跳到错误地址。


指令编码的背景

在 MIPS 架构等体系中,指令地址是 字节对齐的,即每条指令的地址是 4 的倍数(对 32 位字长机器)。
但分支指令的 偏移量字段 是以 半字为单位(2 字节) 表示,这是一种节省位数的设计,因为:

  1. 目标地址通常是相对的,所以用相对偏移量即可。
  2. 偏移量字段的单位选择为半字,已经隐含了「目标地址总是对齐到 2 字节」这一假设。

偏移量设计如何保证正确性?

因为分支指令的偏移量以半字单位编码,而地址计算需要字节单位:

  • 如果偏移量没有左移 1 位,计算出来的地址就会错
  • 左移 1 位后,偏移量的单位从半字变成了字节,与实际的程序计数器(PC)的单位一致,计算出的目标地址是正确的

详细举例分析

假设以下场景:

  1. 当前指令地址 PC = 0x00400010
  2. 分支指令的偏移量字段 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 倍?

一个重要的点是 偏移量的实际含义

  1. 指令中的偏移量字段(imm)是一个 相对地址的缩略表示,编码时选择了半字为单位。

    • 偏移量 3 表示「跳过 3 个半字」,但程序计数器和内存地址以字节为单位工作,所以在计算中必须将 3 转换为字节单位,即 3 × 2 = 6 字节。
  2. 偏移量左移 1 位后,并不是修改了偏移量的实际大小,而是 调整了表示单位,从半字单位变成了字节单位。


总结

  • 左移 1 位不会导致跳到非目的地址,因为偏移量的原始编码就是以半字单位设计的。
  • 左移 1 位只是为了让偏移量与地址的单位一致,确保计算结果正确。
  • 如果不左移 1 位,反而会导致跳转错误,因为单位不匹配。
This post is licensed under CC BY 4.0 by the author.