gpt4 book ai didi

c++ - 将C++函数转换为MIPS汇编

转载 作者:行者123 更新时间:2023-11-30 05:31:48 26 4
gpt4 key购买 nike

我需要将此C++函数转换为MIPS程序集:

int set(int a[], int n, int v)
{
int i;
i = 0;
do {
a[i++] = v;
} while ( i < n);
return i;
}

其中数组的地址在 $a0中,n在 $a1中,v在 $a2中。
这是我尝试在MIPS中编写函数的尝试,但是我得到“指令引用在0x00400054处 undefined symbol ”。在主要(由我的教授提供)中,有一个调用 jal set,应该调用 set函数,我很确定我的错误与此有关。我也不知道我是否成功转换了函数。这是我的尝试:
.text
set:
li $t0, 0
addi $t0, $t0, 0
sll $t1, $t0, 2
add $t1, $t1, $a0
L1: lw $t1, 4($t1)
sw $a2, 0($t1)
blt $t0, $a1, L1

如果这很重要,我正在使用QTSPIM。感谢您的帮助,如果您有任何建议可以进行MIPS编程,那也很好。

更新:

现在正在链接文件,但是出现“在PC = 0x004000f0发生异常”和“读取的数据/堆栈中的地址错误:0x00000000”的无限循环。这是我的更新文件:
`.text
.globl set
set: addi $t0, $t0, 0 #i = 0;
sll $t1, $t0, 2 #offsets 4 * i
add $t1, $t1, $a0 #pointer to a[i]
L1: lw $t1, 4($t1) #a[i++]
sw $a2, 0($t1) #a[i++] = v
blt $t0, $a1, L1 #continue the loop as long as i < n`

为什么我的代码必须在 .globl中? .text的目的是什么?

最佳答案

好的,有一些问题。

更正的代码在底部。实际上,有两种方法可以做到这一点,具体取决于从C代码进行转换的字面意义。您的概念性问题的一部分可能是您试图将两种方法的部分结合起来。

您基于注释反馈(作为重复)(例如li然后addi)将原始代码中的前两个指令折叠为一个。但是,如果仅使用一个,则li是正确的,因为addi将寄存器添加到自身中,但是您不能依赖于初始值为零。
sll正在移动其中值为零的寄存器,因此inst没有执行任何操作。

要使用t1加载a0,您可以使用add $t1,$a0,0 [或add $t1,$a0,$zero]
lw没有任何作用[C代码不会加载a,所以为什么要使用asm?]。

但是,由于循环仍然无法正常工作,我对此进行了一些调整。

您的blt之后没有返回任何内容,因此即使循环起作用,它也会“掉在世界的边缘”。每个调用的asm例程[类似于jal set调用的例程]必须具有显式的return语句,即jr $ra
注意:在MIPS asm中,被调用方可以修改a* [参数寄存器],因此请在a0而不是t1上循环(即,调用方希望将其丢弃)

无论如何,这是更正后的代码[请原谅免费的样式清理]:

    .text

.globl set
set:
li $t0,0 # i = 0
L1:
sw $a2,0($a0) # a[i] = v
add $a0,$a0,4 # advance pointer
add $t0,$t0,1 # ++i
blt $t0,$a1,L1 # continue the loop as long as i < n
jr $ra # return

如果您原来的C函数是这样的:
int
set(int *a, int n, int v)
{
int *end;

end = a + n;
for (; a < end; ++a)
*a = v;

return n;
}

然后,这将是更直译的翻译:
    .text

.globl set
set:
sll $a1,$a1,2 # convert int count to byte length
add $a1,$a0,$a1 # end = a + length

L1:
sw $a2,0($a0) # *a = v
add $a0,$a0,4 # ++a
blt $a0,$a1,L1 # continue the loop as long as a < end

jr $ra # return

IMO,这两种方法都是原始C函数的可接受实现。第一个是更文字的,因为它保留了索引变量 i的[概念]。但是,它还有一条额外的指令,第二条指令则没有。

无论优化器正在翻译哪个C函数,优化器都可能会生成相同的代码(即第二个asm)(MIPS不像 x86 asm那样具有强大的索引寻址模式)。

因此,“正确”可能取决于您的教授有多强。

旁注:请注意两个示例之间的样式更改。也就是说,将代码更改放在一边,添加一些空行以提高清晰度。

为了完整起见,这是我在测试时创建的 main函数:
    .data
arr: .space 400 # allocate more space than count

.text

.globl main
main:
la $a0,arr # get array pointer
li $a1,10 # get count
li $a2,3257 # value to store

jal set

li $v0,1 # exit syscall number
syscall

更新:

If in the loop a[i++] = v, would I place the add $t0, $t0, 1 line before the sw $a2, 0($a0)?



不,如果C代码是 a[++i] = v,则可以这样做。也许,最好的方法是首先简化C代码。
a[i++] = v实际上是:
a[i] = v;
i += 1;

而且, a[++i] = v实际上是:
i += 1;
a[i] = v;

现在,C代码行和asm指令行之间是一一对应的。

When would I use the sll? I was reading examples and I noticed people usually do sll $t1, $t0, 2 when they're going to use a counter to go through an array.



是。如果您仔细观察我的第二个实现,它将以这种方式使用 sll。而且,即使给出原始的C代码,这也是我编写循环代码的方式。

Would I use the lw if the C code said something like int x = a[0]?



对,就是这样。

原型(prototype)asm的另一种方法是将C代码转换为“非常笨拙的C”。

也就是说,只有最简单形式的 if: if (x >= y) goto label。甚至 if (x < y) j = 10都超出限制。

没有函数作用域变量或函数参数变量-仅是寄存器名称的全局变量。

没有复杂的表达式。只有简单的像 x = yx += yx = y + z。因此, a = b + c + d太复杂了。

寄存器变量既可以用作整数值,也可以用作字节指针。因此,当添加到用作指针的寄存器时,就像添加到字节指针一样,因此要通过 int数组递增,必须添加 4

字节指针和 int指针之间的实际区别仅在您执行加载/存储操作时才重要: lw/sw表示 intlb/sb表示字节。

因此,这是我的第二个C函数重新编码为“哑巴”:
// RETURNS: number of elements changed
int
set(void)
// a0 -- "a" (pointer to int array)
// a1 -- "n" (number of elements in "a")
// a2 -- "v" (value to set into "a")
{

v0 = a1; // set return value

a1 <<= 2; // convert int count to byte length
a1 += a0; // point to one past end of array

L1:
*(int *)a0 = a2; // store v at current array location
a0 += 4; // point to next array element
if (a0 < a1) goto L1; // loop back until done

return;
}

更新2:

In your first implementation, is add $a0, $a0, 4 the equivalent to using sll in the second implementation?



不完全的。要记住的关键是在C语言中,当我们向一个指针添加一个[或使用 i对其进行索引]时,编译器将生成一个增量/添加指令,该指令会添加该指针定义为的 sizeof类型。

也就是说,对于 int *iptr,指定 iptr += 1将生成 add $a0,$a0,4,因为 sizeof(int)为4。如果我们有 double *dptrdptr += 1,则编译器将生成 add $a0,$a0,8,因为 sizeof(double)为8

C编译器提供了强大的“便利性”,因为它允许数组,指针,索引可以互换使用。

在asm中,我们必须手动执行C编译器将自动为我们执行的操作。

考虑以下内容:我们有一个值,它是数组中元素数量的计数,将其称为 count。现在,我们想知道数组将占用多少字节。我们将其称为 len。这是确定各种类型的C代码:
char *arr;
len = count * sizeof(char);
len = count * 1;
len = count << 0;
// sll $a1,$a1,0

short *arr;
len = count * sizeof(short);
len = count * 2;
len = count << 1;
// sll $a1,$a1,1

int *arr;
len = count * sizeof(int);
len = count * 4;
len = count << 2;
// sll $a1,$a1,2

double *arr;
len = count * sizeof(double);
len = count * 8;
len = count << 3;
// sll $a1,$a1,3

From what I understand, using sll sets i as a counter for ints so it increments i and also iterates the array



否。 sll仅仅是MIPS的“左移逻辑”指令,当您需要使用C的 <<运算符时,就可以使用它。

您在想的是如何使用 sll来实现该效果。

要遍历 int数组,我们将索引增加1,但还必须将数组指针增加4。这就是我的第一个asm示例。终止条件为 index >= count

在第二个asm示例中,我通过将元素计数转换为字节长度(通过 ssl)并添加了数组地址来消除了单独的索引变量。现在 $a1的地址为数组的最后一个元素+ 1,终止条件为 current_address >= last_element_plus_1。请注意,current_address( $a0)仍必须增加4( add $a0,$a0,4)

要记住的重要一件事是,asm指令(尤其是MIPS)很简单(即愚蠢)。他们一次只做一件事。如果一个C赋值语句足够复杂,则它可以生成约20条指令。通过asm指令的组合可以产生更复杂的结果。

关于c++ - 将C++函数转换为MIPS汇编,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35399176/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com