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
,因为每条指令都必须从对齐的地址开始,所以编码两个最低有效位会很浪费,它们总是 00
。 0x100000 * 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 会帮你找到标签的地址。
在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
,因为每条指令都必须从对齐的地址开始,所以编码两个最低有效位会很浪费,它们总是 00
。 0x100000 * 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 会帮你找到标签的地址。