MIPS 汇编程序如何管理标签地址?

How does MIPS assembler manage label address?

MIPS 的汇编程序标签和 J 类型指令如何工作?

我目前正在使用 C++ 制作 MIPS 模拟器,遇到了一个大问题。 MIPS 汇编程序在执行 J 类型指令时究竟如何管理标签及其地址?

假设我们有以下代码。我们还假设 start:0x00400000 开始。代码后的注释表示机器代码将存储在内存中的位置。

start:
        andi $t0, $t0, 0     # 0x0040 0000
        andi $t1, $t1, 0     # 0x0040 0004
        andi $t2, $t2, 0     # 0x0040 0008
        addi $t3, $t3, 4     # 0x0040 000C
loop:
        addi $t2, $t2, 1     # 0x0040 0010
        beq $t2, $t3, exit   # 0x0040 0014
        j loop               # 0x0040 0018

exit:
        addi $t0, $t0, 1000  # 0x0040 002C

据我目前了解,j loop 表达式会将 PC 设置为 0x0040 0010

当J型指令使用32位并且以MSB 6位作为操作码时,它只有26 bits 表示指令地址。那么如何只用26位来表示32位地址系统呢?

用上面的例子,只用24bits就可以表示0x00400010。但是,在参考文献中,文本段位于0x004000000x10000000之间,需要32位来表示。

我试图使用 MARS 模拟器来理解这一点,但是它只是将 j loop 表示为 j 0x00400010 这对我来说似乎是无稽之谈,因为 0x00400010 是 32 位。

我目前的猜测

我目前的猜测之一如下。

汇编程序将loop: 标签的地址保存到某个26 位可达的内存地址中。然后当调用表达式j loop时,标签loop被翻译成包含0x00400010的内存地址例如,0x00400010被保存在0x00300000这样的地址中并且当 j loop 被调用时,loop 被翻译成 0x00300000 并且它能够从 0x00300000 获取值并到达 0x00400010。 (这只是我的猜测之一)

您有很多问题。

首先,让我们尝试区分汇编程序的操作和它生成并由处理器执行的 MIPS 机器代码。

汇编器以两种方式管理标签和地址。首先,它有一个符号 table,就像一个字典,一个 key-value 对的数据结构,其中名称是键和地址(当程序是 运行) 是成对的值。

其次,汇编程序使用位置计数器管理代码和数据部分。每次程序提供一些代码或数据时,该位置计数器都会增加。定义新标签时,当前位置计数器将用作新 key-value 对中的地址值。

处理器永远不会看到标签:它们不执行,也不占用代码或数据中的任何 space。处理器只能看到机器代码指令,在 MIPS 上这些指令都是 32 位宽的。每条机器代码指令都被划分为字段。指令类型或格式在 MIPS 上很简单:I-Type、J-Type 和 R-Type。然后这些格式定义了指令字段,汇编程序遵循这些编码。所有的指令格式共享一个 6 位的操作码字段,这个操作码字段告诉处理器指令是什么格式,因此它有哪些字段,以及如何解释和执行指令的其余部分。

汇编程序从程序集中删除了标签——标签及其名称不存在于二进制程序中。标签定义本身 (label:) 从程序二进制文件中省略,但 标签的使用 被翻译成数字,因此使用标签的机器代码指令将有一些指令字段这是数字,并且汇编程序将为该数字字段提供适当的值,以便达到或以其他方式访问标签所指的内容的效果得以实现。 (标签不再在程序二进制文件中,但标签引用的代码或数据内存仍然存在)。

汇编程序设置分支指令、j 指令和 la/lw 指令,使用数字告诉处理器将程序计数器向前或向后移动多远,或者,某些感兴趣的数据位于什么地址。 lw/la 指令访问数据,这些指令使用 2 x 32 位指令,每条指令包含 16 位感兴趣的地址。在这两条指令之间,它们组合了一个完整的 32 位地址用于数据访问。对于完全到达任何 32 位地址的分支,他们必须以类似的方式(两个指令对)将 32 位地址放在一起并使用 indirect/register 分支。