9

所以,我们正在学校学习 MIPS 架构,我们正在实现 MIPS32 架构。我以为我会使用 GNU cross-binutils 作为汇编程序,但是在处理指令 jal、j 和 jr 时我得到了奇怪的输出。汇编器似乎在错误的地方插入了指令。我不知道为什么会发生这种情况,而且我怀疑 MIPS 汇编器会那么坏,所以我认为这应该会发生。

这是我的虚拟程序集文件:

.section .text
.globl __start

__start:
   addi $a0, $0, 100
   addi $a1, $0, 200 
   jal test

test:
   add $v0, $a0, $a1
   jr $ra

然而,当我反汇编我得到这个输出:

Disassembly of section .text:

00000000 <__start>:
   0:   20040064    addi    a0,zero,100
   4:   0c000003    jal c <test>    <--- Why is jal coming before addi?
   8:   200500c8    addi    a1,zero,200

0000000c <test>:
   c:   03e00008    jr  ra  <--- Why is jr coming before add?
  10:   00851020    add v0,a0,a1
    ...

这是一些建筑怪癖吗?如果是这样,这背后的理由是什么?

编辑:测试添加一些 nop 只是为了见鬼......

.section .text
.globl __start

__start:
   addi $a0, $0, 100
   addi $a1, $0, 200 
   nop
   jal test

test:
   add $v0, $a0, $a1
   nop
   jr $ra

它给了我一些似乎有些正确的东西。

Disassembly of section .text:

00000000 <__start>:
   0:   20040064    addi    a0,zero,100
   4:   200500c8    addi    a1,zero,200
   8:   0c000004    jal 10 <test>
   c:   00000000    nop

00000010 <test>:
  10:   00851020    add v0,a0,a1
  14:   03e00008    jr  ra
  18:   00000000    nop
  1c:   00000000    nop

为什么 jal 和 j 与最后一条指令交换位置?

4

1 回答 1

12

MIPS 有明确的管道危害;紧跟在分支或跳转指令之后的指令将始终被执行(该指令有时被称为“分支延迟槽”)。如果您的代码确实按照您编写的方式组装:

__start:
   addi $a0, $0, 100
   addi $a1, $0, 200 
   jal test

test:
   add $v0, $a0, $a1
   jr $ra

那么该add指令将在jal发生时执行两次:一次在延迟槽中,一次在程序计数器更改实际生效的下一个周期中。

默认情况下,GNU 汇编器会为您重新排序指令:很明显第二个addi必须始终执行,因此它可以与jal指令交换,以便addi移动到延迟槽中。(如果汇编器无法推断出这样做是安全的,它会将 anop插入到延迟槽中。)

如果您不希望它为您重新排序,请添加指令

.set noreorder

在源文件的顶部。在这种情况下,您必须自己处理危险。如果你这样做,我建议对延迟槽进行注释,以便它们突出 - 例如通过添加额外的缩进空间(或两个)。例如:

.set noreorder

__start:
   addi $a0, $0, 100
   jal test
     addi $a1, $0, 200 

test:
   add $v0, $a0, $a1
   jr $ra
     nop
于 2010-09-27T23:15:02.517 回答