将C语言转换为MIPS - 嵌套数组

5
我正在学习MIPS汇编语言,看到这本书中的示例,但在我看来它似乎不正确。如果是错误的话,那么这本书中不是第一个我发现的错误。
变量f和g分别被分配到寄存器$s0和$s1中,数组A和B的基地址分别为$s6和$s7。
C代码示例如下:
f = g - A[B[4]];

对应的 MIPS 汇编代码如下:

lw  $t0, 16($s7)
lw  $s0, 0($t0)
sub $s0, $s1, $s0

我的理解是,上面的MIPS代码将从$t0提供的地址处加载一些随机数据,然后从$s1中减去它,并不访问在$s6中表示的数组的索引$t0

根据我的理解,正确的MIPS汇编代码应该是:

lw  $t0, 4($s7)
add $t0, $t0, $s6
sll $t0, $t0, 2
lw  $s0, 0($t0)
sub $s0, $s1, $s0

我是否正确,这是书中的一个错误还是我理解有误。


编辑:根据Chris Dodd指出的错误,已经修改了MIPS代码的纠正。


3
我认为你是正确的。做得好! - davin
1
你的解释非常好。最大的证明是代码没有提到$s6,所以它无法从数组A中检索元素,这表明代码并没有按照预期执行。 - davin
4
假设数组A每个元素占4个字节(就像数组B一样——您使用偏移量16来获取第4个元素,并使用lw指令获取4个字节),那么您还需要在这里乘以4(或者等效地左移2位)。 - Chris Dodd
好眼力,Chris Dodd,我没发现那个。 - MitMaro
正确。从书中复制的代码最好情况下是未定义的,最坏情况下会产生段错误。 - Sparafusile
3个回答

5

这篇文章是为了任何人(可能是CprE 381的学生)寻找好的例子而写的。原帖作者编辑过的代码仍然是不正确的。第一个load word函数中的偏移量应该是16,如果内存宽度是32位,则可以为4,但那时就不需要进行移位/乘法操作了。假设内存宽度为8位,则需要交换add和shift函数。在原帖作者的代码中,它将A [B [4] / 4]的地址乘以4。先进行移位/乘法操作将得到正确的索引。正确的代码如下:

lw  $t0, 16($s7)   # gets the value of B[4]
                   # offset could be 4 depending on memory width
                   # but then the shift would not be needed
sll $t0, $t0, 2    # this multiplies the index by 4 to get the address offset
add $t0, $t0, $s6  # adds the base address of A and the offset
lw  $t0, 0($t0)    # loads the value at the address
sub $s0, $s1, $t0  # performs subtraction and stores in f

如果有人对16与4的偏移及是否需要进行移位感到困惑,让我来解释一下。如果内存宽度为32位,则一个完整的32位整数可以存储在一个内存位置中。在这种情况下,数组索引与地址偏移量相同。然而,如果内存宽度只有8位(1字节),则32位整数将跨越4个内存位置存储(每个字节一个地址)。这就是为什么需要将索引左移2位(或乘以4)以获得正确的地址偏移量的原因。

0

正如许多人指出的那样,这本书中存在错误。自从发现这个错误后,我发现了几个类似的错误。


-2

但是作者在链接时间之前很可能复制了代码。这将开放链接器在语句中填充A[]的内存地址而不是0的可能性

  lw  $s0, 0($t0)

在最终的可执行文件中,我不知道MIPS是否允许那么大的偏移量(也就是A[]最终被放置的地址范围)。当然,在书中这并不是一个好的解释方式,默默地打破自己的前提条件,通常也不知道发生了什么。


1
这样做是不行的,因为偏移量是有符号的16位字节偏移量,而$s6原则上可以使用完整的32位地址范围。很可能只是书中存在错误。(另外我认为任何MIPS链接器都不会进行此类优化,因为相对偏移量非常麻烦,并且必须处理偏移量过大的情况) - user786653
没有理智的汇编器/链接器会这样做。对于该指令的作用绝对没有任何混淆。那是一个带有寄存器+立即数地址的显式加载指令。这不像高级语言编译器可能插入任何它想要的指令。 - Jeff Mercado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接