MIPS 标签存储位置

MIPS labels storage location

在MIPS中,在使用跳转指令的同时,我们使用了一个标签。

again: nop
    $j again

所以当我们到达跳转指令时,我们用标号again来表示去哪里,用到那里的实际地址的值。我想知道标签 again 存储在哪里。意思是说nop存放在0x00400000,跳转指令在0x00400004。那么again保存在哪里,MIPS怎么知道again指向0x00400000呢?它是否存储在内存映射的动态数据区域? This is the memory map I've been provided for MIPS

我也把造成这个混乱的问题写在下面,以供参考。

给出以下分支(be,bne)和跳转(j)指令的十六进制目标代码。

... # some other instructions
again:  add ... # there is an instruction here and meaning is insignificant
    add ... # likewise for the other similar cases
    beq    $t0, $t1, next
    bne  $t0, $t1, again
    add ...
    add ...
    add ...
next:   j   again

假设标签再次位于内存位置 0x10 01 00 20。如果您认为没有足够的信息来生成代码,请解释。

标签本身没有存储在任何地方。它只是 assembler/linker 的符号地址。跳转 j again 指令操作码确实存储实际结果地址,如数字。

链接器会将所有目标文件粘合在一起,合并目标文件中的所有符号并填充正确的相对地址+为OS加载程序创建重定位table,生成executable文件.

OS加载executable时也会加载重定位table,modify/fill-up根据实际地址使用绝对地址的指令,其中加载二进制文件,然后抛出重定位 table,并执行代码。

所以标签只是 "source thing" 给程序员,特定固定内存地址的别名,避免程序员计算实际指令操作码大小和计算头中的跳转偏移量,或内存变量地址。

您可能希望在编译某些汇编源代码时从汇编程序中检查 "list file"(通常是 /l 开关),以查看实际产生的 machine code 字节(none 用于标签)。


你的 "task" 代码在 0x00400000 编译时看起来像这样(我将那些 add 设置为执行 t1=t1+t1 以在那里有任何东西):

 Address    Code        Basic                     Source

0x00400000  0x01294820  add ,,          4     add  $t1,$t1,$t1
0x00400004  0x01294820  add ,,          5     add  $t1,$t1,$t1
0x00400008  0x11090004  beq ,,0x00000004  6     beq  $t0, $t1, next
0x0040000c  0x1509fffc  bne ,,0xfffffffc  7         bne  $t0, $t1, again
0x00400010  0x01294820  add ,,          8     add  $t1,$t1,$t1
0x00400014  0x01294820  add ,,          9     add  $t1,$t1,$t1
0x00400018  0x01294820  add ,,          10    add  $t1,$t1,$t1
0x0040001c  0x08100000  j 0x00400000          11   next:   j   again

如您所见,每条真正的指令确实会产生32位值,有时称为"opcode"(操作码),该值在"Code"列中可见。 "Address" 列表示,当加载 executable 并准备执行时,此值存储在内存中的位置。 "Basic" 列显示了从操作码反汇编的指令,最后位置是 "Source".

现在看看条件跳转是如何将相对跳转值编码成16位的(beq , 操作码是0x1109,另外16位0x0004是16位符号扩展值"how much to jump").该值表示远离 "current position" 的指令数,其中当前是后续指令的地址,即

0x0040000c + 0x0004 * 4 = 0x0040001c = target address

*4,因为在 MIPS 上每条指令恰好是 4 个字节长,内存寻址是按字节而不是按指令进行的。

下一个bne也一样,操作码本身是0x1509,偏移量是0xfffc,也就是-4。 =>

0x00400010 + (-4) * 4 = 0x00400000

绝对跳转使用不同的编码,它是6位操作码0b000010xx(xx是存储在第一个字节中的两位地址和j操作码,在这个例子中它们是零) 26b 地址除以 4 0x0100000,因为每条指令都必须从对齐的地址开始,所以编码两个最低有效位会很浪费,它们总是 000x100000 * 4 = 0x00400000 ...我懒得检查它在 MIPS 上是如何工作的,但我认为 j 定义了位 2-27,0-1 是零,28-31 是从当前复制的pc 也许吧?使 CPU 能够在完整的 4GiB 地址范围内工作,但可能有一些特殊的方法如何在不同的 "banks" 之间跳转(pc 的高 4 位))..我不是当然,我从未编写过 MIPS 代码,所以我没有阅读 CPU 规范。

无论如何,如果你说 again:0x10010020,所有这些都可以重新计算以遵循生成功能代码准备在 0x10010020 执行(尽管j 会很棘手,您必须确定地知道总地址是如何组成的,是否复制了高 4 位等等)。

顺便说一句,真正的 MIPS CPU 会延迟分支(即总是执行分支跳转后的下一条指令,同时评估条件,并在下一条指令之后发生跳转),我认为用于计算目标地址的 pc 也是 1 条指令 "later",因此真正 MIPS 的正确代码应该是 beq 在第二条 add 之前,但是相对偏移量仍为 0x0004。 :) 简单吧?如果它对你没有意义,请检查 MARS 设置(延迟分支的模拟默认关闭,以免混淆学生),并搜索 google 以获得更好的解释。很好笑 CPU 就是 MIPS。 :)

每个标签对应于内存中的唯一地址。因此,在您的示例中,并且与您所说的一致,如果 nop 指令存在于 0x00400000 处,则 again 将对应(不是指向 - 稍后会详细说明)到同一地址。

标签可以同时存在于文本和数据段中。但是,在您的示例中,标签显示在 .text: 段中。因此,它代表指令的地址而不是变量。

这是重要的区别:

标签是大多数 ISA 的一部分,使编写汇编对人类来说更容易。但是,重要的是要记住,汇编 不是 代码的最终形式。换句话说,在二进制表示中,您的标签将不再是一个标签。

所以,这就是将要发生的事情:

汇编程序将识别与每个标签指令关联的内存地址。让我们保留 0x00400000 的 运行 示例。然后,在每个跳转指令中,它将获取该地址并用它来替换操作码中的标签。 Poof,没有更多的标签,绝对没有指针(这意味着我们将在内存中有另一个存储内存地址的地方)。

当然,内存地址本身对应于您示例中文本段中的一个点,因为它与一条指令匹配。

简单地说,标签的存在是为了让我们的生活更轻松。但是,一旦它们被组装起来,它们就会转换为它们标记的 instruction/variable 的实际内存地址。

标签到相应地址的转换是由您使用的代码汇编器或MIPS 模拟器完成的,例如,MARS 是一个MIPS 模拟器,因此MARS 正在执行该转换。 MARS 会帮你找到标签的地址。