2

在 MIPS 中,在使用跳转指令时,我们使用标签。

again: nop
    $j again

所以当我们到达跳转指令时,我们使用标签again来显示去哪里以及使用的实际地址的值。我想知道标签再次存储在哪里。也就是说,saynop存储在0x00400000,跳转指令在0x00400004。那么again,MIPSagain是如何知道指向 0x00400000 的呢?它是否存储在内存映射的动态数据区域中?这是我为 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。如果您认为您没有足够的信息来生成代码说明。

4

3 回答 3

2

标签本身不存储在任何地方。它只是汇编器/链接器的符号地址。跳转j again指令操作码确实存储了实际的结果地址,就像一个数字一样。

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

操作系统在加载可执行文件时还将加载重定位表,根据实际地址修改/填充使用绝对地址的指令,二进制文件被加载的位置,然后丢弃重定位表,并执行代码。

因此,标签只是程序员的“源代码”,是特定固定内存地址的别名,以使程序员免于计算实际指令操作码大小和计算头部或内存变量地址中的跳转偏移量。

在编译一些汇编源代码时,您可能希望从汇编器中检查“列表文件”(通常是/l开关),以查看生成的实际机器代码字节(标签没有)。


您在编译时的“任务”代码0x00400000如下所示(我将它们设置add为 t1=t1+t1 以在其中包含任何内容):

 Address    Code        Basic                     Source

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

如您所见,每条实际指令都会产生 32 位值,有时称为“操作码”(操作码),该值在“代码”列中可见。“地址”列表示,该值存储在内存中的位置,何时加载可执行文件并准备执行。“基本”列显示了从操作码反汇编回来的指令,最后一个位置是“源”列。

现在看看条件跳转如何将相对跳转值编码为 16 位(beq $8, $9操作码是0x1109,而其他 16 位0x0004是 16 位符号扩展值“要跳转多少”)。该值表示远离“当前位置”的指令数,其中当前是后续指令的地址,即。

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

*4,因为在 MIPS 上,每条指令的长度正好是 4 个字节,并且内存寻址是按字节工作的,而不是按指令工作的。

next 也是一样bne,opcode 本身是0x1509,offset 是0xfffc,也就是-4。=>

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

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

无论如何,如果您说again:is at 0x10010020,所有这些都可以重新计算以遵循准备好执行的生产功能代码0x10010020(虽然这j很棘手,但您必须确定总地址是如何组成的,如果高4位被复制或什么)。

BTW,真正的MIPS CPU会延迟分支(即总是执行分支跳转后的下一条指令,同时评估条件,跳转发生在下一条指令之后),我认为pc用于计算目标地址的也是1指令“稍后”一个,因此真正 MIPS 的正确代码将beq在第二个之前具有该指令add,但相对偏移量仍为0x0004. :) 简单吧?如果这对您没有意义,请检查 MARS 设置(延迟分支的模拟默认关闭,以免混淆学生),并搜索谷歌以获得更好的解释。漂亮的小有趣 CPU,就是 MIPS。:)

于 2017-03-05T17:31:07.857 回答
2

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

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

这是重要的区别:

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

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

汇编器将识别与每个标签指令相关的内存地址。让我们继续运行 0x00400000 的示例。然后,在每个跳转指令中,它将获取该地址并使用它来替换操作码中的标签。,没有更多的标签,也绝对没有指针(这意味着我们将在内存中拥有另一个存储内存地址的位置)。

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

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

于 2017-03-05T17:32:02.083 回答
0

The conversion of label to its corresponding address is done by the code assembler or MIPS simulator you are using, for example, MARS is a MIPS simulator, so MARS is doing that conversion. MARS will find the address of the label for you.

于 2019-04-06T16:22:08.033 回答