使用跳转(和链接)指令的奇怪的MIPS汇编程序行为

时间:2010-09-27 20:15:20

标签: assembly mips disassembly binutils

因此,我们正在学校学习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交换位置的最后一条指令?

1 个答案:

答案 0 :(得分:9)

MIPS存在明确的管道危害;紧接在分支或跳转指令之后的指令将始终被执行(该指令有时被称为“分支延迟槽”)。如果您的代码与您编写的代码完全一致:

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

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

然后add指令将在jal发生的时间内执行两次:一次在延迟槽中,一次在下一个周期时,程序计数器更改实际生效。< / p>

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

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

nop

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

.set noreorder