我目前正在阅读David Patterson和John Hennesy的书,标题为"计算机组织与设计 - 第4版"。在书中,书中说:
由于所有MIPS指令的长度均为4个字节,因此MIPS通过让PC相对寻址引用 单词到下一条指令,而不是字节数。
当我读到这句话时,我就去了MARS模拟器,看看它在实践中。令我惊讶的是,我观察到$pc寄存器不遵循该规则,并且始终存储字节内存地址。
在执行位于指令地址中的以下指令之前,$pc寄存器不应该是这样的吗?
Instruction Address | $pc Content
|
0x00400000 | 0x00100000
0x00400004 | 0x00100001
0x00400008 | 0x00100002
.... | ....
另一种看待这个问题的方式:PC 的低 2 位固定在0
.
根据您的建议,高 2 位将固定为0
.jr $ra
或其他跳转到寄存器指令也必须左移寄存器,而不是简单地设置$pc = function-pointer
。
(否则,差异在体系结构上是可见的,并且必须将代码作为数据访问,反之亦然,才能从数据地址转换为同一单词的代码地址。
正如Jester指出的那样,$pc
是指向指令字的普通指针,就像MIPS习惯于处理一样。 MIPS使用字节可寻址存储器,但字加载必须对齐(直到MIPS32r6)。 因此,$pc
增量为 4,而不是每次使用时都按 4缩放。
实际设计唯一需要的缩放是分支(I型)和跳跃(J型)的即时缩放。 请参阅如何计算跳转目标地址和分支目标地址?了解其工作原理。 这只是您将即时位连接到加法器的位置问题,使较低的位为零。 它只发生在这些指令的解码中;其他一切都仅适用于普通字节地址。
...通过让 PC 相对寻址引用下一条指令的字数而不是字节数来延长分支的距离。
这句话的意思只有:
存储在地址X的指令0x1000nnnn(b nnnn
)将跳转到地址X+4+4*nnnn
的指令,而不是地址X+4+nnnn
的指令。
这句话没有提到PC寄存器及其价值本身。
我观察到
$pc
寄存器不遵循该规则,并且始终存储字节内存地址。在执行位于指令地址中的以下指令之前,
$pc
寄存器不应该是这样的吗?
这里的问题是:$pc
寄存器到底是什么?
在某些CPU(如ARM)上,有些指令将以MIPS汇编语言编写为addu $t0,$t0,$pc
。谈论这样的CPU,很容易回答这个问题:
$pc
寄存器的值是在执行指令addu $t0,$t0,$pc
时将添加到$t0
的值。
在真正的MIPS CPU(不是MIPS仿真器)上,PC寄存器是某种存储器(30位锁存器),可以容纳30位信息。
但是,当谈论存储在某些内存中的信息时,我们必须定义如何解释信息:
10000111
位可以解释为 135(无符号)、87(BCD)、-121(二的补码)、-120(一补码)、-7(符号和绝对)、SOME_ENUM_CONSTANT
(枚举)、-0.09、0.7、1.35(各种浮点和定点变体)......
PC寄存器中0000...011100
的位指向地址0x70处的指令,这是明确定义的。但是,没有定义此值是否必须写为PC=0x1C
或PC=0x70
。
因此,一些MIPS仿真器可能会显示$pc=0x400000
,而另一个可能会显示PC寄存器中相同值的$pc=0x100000
!
但是,我认为(几乎)所有MIPS模拟器都会显示$pc=0x400000
,因为用户对指令指向的地址感兴趣。