在MIPS中,在使用跳转指令时,我们使用标签。
again: nop
$j again
因此,当我们到达跳转指令时,我们使用标签again
来显示去哪里以及使用的实际地址的值。我想知道再次存储标签的位置。意思是,nop
存储在0x00400000,跳转指令位于0x00400004。哪里,然后是again
保留,MIPS如何知道again
指向0x00400000?它存储在存储器映射的动态数据区域中吗? This is the memory map I've been provided for MIPS
我还在下面列出了导致这种混淆的问题,以供参考。
为以下分支(be,bne)和jump(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.如果您认为没有足够的信息来生成代码说明。
每个标签对应于内存中的唯一地址。因此,在您的示例中,并且与您所声明的内容一致,如果nop
指令存在于0x00400000,则again
将对应(不是指向 - 在一秒钟内更多)指向同一地址。
标签可以存在于文本和数据段中。但是,在您的示例中,标签显示在.text:
段中。因此,它表示指令的地址而不是变量。
这是重要的区别:
标签是大多数ISA的一部分,使人们更容易编写文字。但是,重要的是要记住汇编不是代码的最终形式。换句话说,在二进制表示中,您的标签将不再是标签。
所以,这将是会发生的事情:
汇编器将识别与每个标签指令相关的存储器地址。让我们保持运行的0x00400000示例。然后,在每个跳转指令中,它将获取该地址并使用它来替换操作码中的标签。噗,没有更多的标签,绝对没有指针(这意味着我们将在内存中存储另一个存储内存地址的位置)。
当然,内存地址本身对应于示例中文本段中的一个点,因为它与指令匹配。
简单地说,标签的存在使我们的生活更轻松。但是,一旦它们被组装,它们就会被转换为它们标记的指令/变量的实际存储器地址。
标签本身不存储在任何地方。它只是汇编器/链接器的符号地址。跳转j again
指令操作码确实存储了实际产生的地址,就像数字一样。
链接器将所有目标文件粘合在一起,合并目标文件中的所有符号并填充正确的相对地址+为OS加载程序创建重定位表,生成可执行文件。
加载可执行文件时的OS还将加载重定位表,根据加载二进制文件的实际地址修改/填充使用绝对地址的指令,然后抛出重定位表,并执行代码。
因此标签只是程序员的“源代码”,是特定固定内存地址的别名,可以节省程序员从计算实际指令操作码大小和计算磁头或内存变量地址中的跳转偏移量。
您可能需要检查汇编程序中的“列表文件”(通常是/l
开关),同时编译一些汇编源代码,以查看生成的实际machine code字节(标签中没有)。
在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位值,有时称为“操作码”(操作码),该值在“代码”列中可见。 “Address”列表示,此值存储在内存中,当加载可执行文件并准备执行时。 “Basic”列显示从操作码反汇编的指令,最后一个位置有“Source”列。
现在看看条件跳转如何将相对跳跃值编码为16位(beq $8, $9
操作码是0x1109
,其他16位0x0004
是16位符号扩展值“跳多少”)。该值表示远离“当前位置”的指令数,其中当前是下一指令的地址,即。
0x0040000c + 0x0004 * 4 = 0x0040001c = target address
* 4,因为在MIPS上,每个指令正好是4个字节长,并且存储器寻址每个字节工作,而不是每个指令。
下一个bne
也是如此,操作码本身是0x1509
,偏移量是0xfffc
,那是-4。 =>
0x00400010 + (-4) * 4 = 0x00400000
绝对跳转使用不同的编码,它是6位操作码0b000010xx
(xx是与j
操作码一起存储在第一个字节中的两位地址,在本例中它们是零)接着是26b地址除以四个0x0100000
,因为每个指令必须启动在对齐的地址,因此编码两个最低有效位将是浪费,它们将始终是00
。 0x100000 * 4 = 0x00400000
...我太懒了,无法检查MIPS是如何工作的,但我认为j
定义位2-27,0-1是零,28-31是从当前的pc
复制的可能吗?使CPU能够在完整的4GiB地址范围内工作,但是可能有一些特殊的方法如何在不同的“库”(pc
的高4位)之间跳转。)我不确定,我从来没有为MIPS编写代码,所以我没有阅读CPU规格。
无论如何,如果你说again:
在0x10010020
,所有这些都可以重新计算,以便生成功能代码准备在0x10010020
执行(虽然j
会很棘手,你必须知道,总地址如何组成,如果复制高4位或什么)。
顺便说一句,真正的MIPS CPU确实延迟了分支(即,总是执行跳转后的下一条指令,同时评估条件,并且在下一条指令之后跳转),我认为用于计算目标地址的pc
也是1个指令“稍后”一个,所以真正的MIPS的正确代码将在第二个beq
之前有add
,但相对偏移仍然是0x0004
。 :)简单呃?如果它对您没有意义,请检查MARS设置(默认情况下,延迟分支的模拟已关闭,以免混淆学生),并搜索谷歌以获得更好的解释。很好的小搞笑CPU,那是MIPS。 :)
标签到其相应地址的转换是由您正在使用的代码汇编程序或MIPS模拟器完成的,例如,MARS是MIPS模拟器,因此MARS正在进行该转换。 MARS将为您找到标签的地址。