MIPS加载地址la并不总是使用寄存器$ 1?
请参阅编辑部分以获取我的解释。
这有点长,很难说明。 但我很欣赏花时间阅读本文。 请多多包涵。
假设我有这个:
.data
str1: .asciiz "A"
str2: .asciiz "1"
myInt:
.word 42 # allocate an integer word: 42
myChar:
.word 'Q' # allocate a char word
.text
.align 2
.globl main
main:
lw $t0, myInt # load myInt into register $t0
lw $t3, str1 # load str1 into register $t3
lw $t4, str2 #load str2 into register $t4
la $a0, str1 # load address str1
la $a1, str2 # load address str2
然后在SPIM中,用户文本段为
User Text Segment [00400000]..[00440000]
[00400000] 8fa40000 lw $4, 0($29) ; 183: lw $a0 0($sp) # argc
[00400004] 27a50004 addiu $5, $29, 4 ; 184: addiu $a1 $sp 4 # argv
[00400008] 24a60004 addiu $6, $5, 4 ; 185: addiu $a2 $a1 4 # envp
[0040000c] 00041080 sll $2, $4, 2 ; 186: sll $v0 $a0 2
[00400010] 00c23021 addu $6, $6, $2 ; 187: addu $a2 $a2 $v0
[00400014] 0c100009 jal 0x00400024 [main] ; 188: jal main
[00400018] 00000000 nop ; 189: nop
[0040001c] 3402000a ori $2, $0, 10 ; 191: li $v0 10
[00400020] 0000000c syscall ; 192: syscall # syscall 10 (exit)
[00400024] 3c011001 lui $1, 4097 ; 23: lw $t0, myInt # load myInt into register $t0
[00400028] 8c280004 lw $8, 4($1)
[0040002c] 3c011001 lui $1, 4097 ; 25: lw $t3, str1 # load str1 into register $t3
[00400030] 8c2b0000 lw $11, 0($1)
[00400034] 3c011001 lui $1, 4097 ; 27: lw $t4, str2 #load str2 into register $t4
[00400038] 8c2c0002 lw $12, 2($1)
[0040003c] 3c041001 lui $4, 4097 [str1] ; 29: la $a0, str1 # load address str1
[00400040] 3c011001 lui $1, 4097 [str2] ; 31: la $a1, str2 # load address str2
[00400044] 34250002 ori $5, $1, 2 [str2]
我知道lw是一个伪代码,所以需要将它分解为两条指令。 我理解这部分。 我们使用数据段的入口地址作为“基址指针”并相对访问其他数据(包括第一个数据)。
我还观察到str1和str2的加载地址使用了两个不同的寄存器: $ 4和$ 1 。 $ 4是$ a0。 这是为什么?
如果我交换了最后两条指令,在SPIM上我可以看到
...
[0040003c] 3c011001 lui $1, 4097 [str2] ; 31: la $a1, str2 # load address str2
[00400040] 34250002 ori $5, $1, 2 [str2]
[00400044] 3c041001 lui $4, 4097 [str1] ; 32: la $a0, str1 # load address str1
那么,为什么加载地址很奇怪呢? 为什么str2使用$ 1 ? 我怎样才能解释lui $ 1,44097 [str2]和lui $ 4,4097 [str1]是如何不同的?
PS:有人可以向我解释为什么我们需要括号[str2]吗?
lui,$ 1,4097,[str2]只推送数据段的入口地址来注册$ 1。 即0x10010000。
非常感谢你!
编辑
我重写了整个脚本以简化情况。
脚本:http://pastebin.com/BHh89iqt文字细分:http://pastebin.com/t2eDEs1f
让我提醒你我们用伪指令编写,而不是真正的MIPS机器代码。 也就是说, “lw”,“jal”,“addi”等都是伪指令。
例如,lw(加载词)分解为两个机器指令(查看文本分段):
lui $1, 4097 ; 23: lw $t0, myInt # load myInt into register $t0
lw $8, 4($1)
MIPS是32位的,因此我们将其分解为两条指令。 寻址32位地址的总数将产生43位指令集。这就是为什么我们分解成2部分的原因。 标签是指向我们分配的东西的内存地址。
MIPS只能读取lw $ rt,offset($ rs)形式的指令。 因此,大多数加载指令都遵循这种方法,并使用$ at将涉及标签的伪指令转换为MIPS机器指令。
这很简单。 对于la load address来说有点棘手。 请注意最后四条加载地址指令。 MIPS将它们转换为:
[0040003c] 3c041001 lui $4, 4097 [str1] ; 27: la $a0, str1 # load address str2
[00400040] 3c011001 lui $1, 4097 [str2] ; 28: la $a0, str2 # load address str1
[00400044] 34240002 ori $4, $1, 2 [str2]
[00400048] 3c011001 lui $1, 4097 [str2] ; 30: la $a0, str2 # load address str2
[0040004c] 34240002 ori $4, $1, 2 [str2]
[00400050] 3c041001 lui $4, 4097 [str1] ; 31: la $a0, str1 # load address str1
$ 4是指$ a0。 如果您查看说明,我交换了前两个加载指令,结果是最后两条指令。 我故意这样做来说明奇怪的行为:在交换之前,lui使用$ 4来存储str1的地址,但是如果我想要加载str2的地址,我将使用$ at,然后应用偏移量。
我无法弄清楚为什么昨天晚上,刚才我意识到这样做是因为编译器足够聪明,知道str1是数据段中的第一个数据,所以不需要转换任何东西。
然而,这也很奇怪,因为编译器如何知道在哪个字节停止打印字符串? (如果我们想打印一个字符串......)
我的猜测是:空字符来终止打印。
无论如何。 我想这只是MIPS使用的一个约定。
第二编辑
事实上,如果你只是在str1之上添加一个新数据,你会发现我的解释是正确的。
新脚本:http://pastebin.com/8DuzFrk0
新的文本段:http://pastebin.com/YXbvzc4z
我只将myCharB添加到数据段的顶部。
[0040003c] 3c011001 lui $1, 4097 [str1] ; 29: la $a0, str1 #
load address str2
[00400040] 34240004 ori $4, $1, 4 [str1]
[00400044] 3c011001 lui $1, 4097 [str2] ; 30: la $a0, str2 #
load address str1
[00400048] 34240006 ori $4, $1, 6 [str2]
我还观察到str1和str2的加载地址使用了两个不同的寄存器:$ 4和$ 1。 $ 4是$ a0。 这是为什么?
那么,谁在乎? xD这是内部SPIM实现,只要不破坏MIPS ABI,就可以使用任何寄存器。 我只是建议你不要太依赖伪指令来确定哪些寄存器已经改变/它们保存了什么值。 通常LW不是一个伪指令,但是就像你使用它的方式一样。
有人可以向我解释为什么我们需要括号[str2]吗?
你不需要任何括号。 这只是程序员为了显示此指令正在加载str2地址的SPIM信息。 这不是大会的一部分。
lui,$ 1,4097,[str2]只推送数据段的入口地址来注册$ 1。 即0x10010000
实际上它只加载了1美元的上半部分。 恰好发生的是低半字是纯粹的零。 请记住,LUI不会修改较低的半字,因此您必须确保它保存您想要的值(重置寄存器或使用LI)。
然而,这也很奇怪,因为编译器如何知道在哪个字节停止打印字符串? (如果我们想打印一个字符串......)
无效终止,正如你猜对的那样。
我想这只是MIPS使用的一个约定。
这比MIPS更古老。 MIPS没有对此做任何定义,或者是其他架构。 这是数据处理,它在OS的上层定义。 在这种情况下,它自己的系统调用是SPIM约定。 无论如何,以null结尾的字符串很常见。 C编程语言对字符串使用如此。
链接地址: http://www.djcxy.com/p/19205.html上一篇: MIPS load address la doesn't always use register $1?
下一篇: Load half word and load byte in a single cycle datapath