我正在研究MIPS指令,这个问题让我感到困惑,因为MIPS文档似乎说的不同于提供的答案。这是问题和答案:
在位置
dump
处的指令中引用和/或更改了哪些寄存器?0x5000
回答:
操作码=
0x5000 : 0x0140F809
,R类型,功能= 0x09(0x00
),Rs = 10(jalr
)跳转到
中的地址$t2
将
$t2
放入0x5004
然而,从文档中可以看出,在寄存器31($ra
)中,它将PC + 4放入。因此,由于指令在地址0x5000处执行,因此PC应该是0x5004对吗?所以$ra
指令不应该将0x5004 + 4或0x5008放入PC而不是0x5004?
对我而言,它应该跳回到0x5004是有意义的,因为这在技术上是跳转后的下一条指令,但是文档明确地说R [31] = PC + 4所以它让我有点困惑,这会是x5008。谢谢!
答案 0 :(得分:2)
你必须考虑的是分支延迟时隙。
首先让我们处理它们关闭的情况。这是spim
和mars
等模拟器的默认设置。事情很简单:
5000: jalr $10 # (1) $31 will have 5004
5004: nop # (2) this executed upon return
这是大多数架构的工作方式。
但是,mips有[上述]分支延迟时隙。
如果[在模拟器中]或真实硬件启用了延迟,则在每次传输控制指令(例如,分支,跳转,jal,jalr)之后是延迟槽中的单个指令是无条件执行之前分支实际被采取[或不]:
5000: jalr $10 # (1) $31 will have 5008
5004: nop # (2) this executed _before_ branch taken
5008: nop # (3) this executed upon return
因此,有效执行顺序实际上是(2),(1),(3)。
在一般情况下,您有三个步骤:
5000: beqz $10,foobar # (1) conditional branch to foobar
5004: nop # (2) executed _before_ branch taken
5008: nop # (3) executed _after_ if branch _not_ taken
有效执行顺序将再次为(2),(1)。然后,如果分支是,则执行foobar
的第一条指令[如果分支是 ],或者5008
(3)处的指令将被执行不采取。
好的,你可能会问为什么?
在早期的MIPS芯片中,预取了指令。例如,循环N + 1的指令在循环N中被预取[并且可能是预解码的](一个循环延迟)。
因此,在周期N,指令 execution 单元正在执行在周期N-1中取出的指令(例如5000),指令预取单元正在取出下一个指令(在5004)。它们与一个周期延迟重叠。在周期N + 1中,执行单元正在执行预取指令(在5004),并且预取单元正在预取下一条指令(在5008)。
这很有效,直到遇到控制指令的条件转移。
如果没有延迟槽,处理器将不得不停止,并且在执行与分支相同的循环中预取的分支之后的指令将被浪费。使用延迟槽执行时,通常可以使用有用的东西填充插槽,因此不需要浪费预取。
但是,它确实使事情变得更复杂。