Assembly 参数数目不定的函数MIPS
我是MIPS的新手,如果这是个愚蠢的问题,请原谅。 我的任务是做以下练习,如果有任何想法和出发点,我将不胜感激 我应该创建一个函数,它接收参数n(数字的数量),然后接收n个数字,然后处理返回所述数字的总和,并在堆栈中返回它。 我怎么开始呢?我认为这个函数可能有4个以上的数字,而实际数字的数量不同这一事实让我感到困惑。 函数参数应该如下所示:(int n,int number1,int number2…等等) 我是否可以将数字存储在堆栈中,然后将堆栈用作函数中的参数?如果是,我会怎么做 更新: 因此,我现在的想法(在我得到的帮助下)是这样的:Assembly 参数数目不定的函数MIPS,assembly,mips,mars-simulator,Assembly,Mips,Mars Simulator,我是MIPS的新手,如果这是个愚蠢的问题,请原谅。 我的任务是做以下练习,如果有任何想法和出发点,我将不胜感激 我应该创建一个函数,它接收参数n(数字的数量),然后接收n个数字,然后处理返回所述数字的总和,并在堆栈中返回它。 我怎么开始呢?我认为这个函数可能有4个以上的数字,而实际数字的数量不同这一事实让我感到困惑。 函数参数应该如下所示:(int n,int number1,int number2…等等) 我是否可以将数字存储在堆栈中,然后将堆栈用作函数中的参数?如果是,我会怎么做 更新: 因
sum:
addu $t3,$sp,16 #add to t3 address of sp+16
addu $a1,$a1,$a2 #adding sum to a1,a1 is first element a2 second and a3 third
addu $a1,$a1,$a3
li $t0,4 #start with i=4
bge $t0,$a0,end_for #while i<n
lw $v0,0($t3) #load v0 with value in stack
addu $a1,$v0,$a1 #add to sum
addi $t3,$t3,4 #increment stack and go up for next element
addi $t0,$t0,1
end_for:
li $v0,1
move $a0,$a0
syscall
jr $ra
sum:
添加$t3,$sp,16#添加到sp+16的t3地址
addu$a1,$a1,$a2#将总和加在a1上,a1是第一个元素,a2是第二个元素,a3是第三个元素
加成$a1、$a1、$a3
li$t0,4#从i=4开始
bge$t0,$a0,end_for#而在正常的MIPS调用约定中,第4个之后的args将已经存储在调用堆栈上,由调用方放置在那里
标准的调用约定在堆栈arg之前保留填充,您可以在其中存储寄存器arg以创建所有arg的连续数组,另见
这在x86-64 Windows中通常称为“阴影空间”。但由于MIPSjal
不将任何内容存储到内存中(不像x86在堆栈上推送返回地址,MIPS将返回地址放在$lr
中),即使调用约定不包括此阴影空间,函数仍然可以先调整SP,然后存储与堆栈参数相邻的寄存器参数。因此,我能看到的唯一好处是,在不必调整堆栈指针的情况下,为微小的函数提供额外的暂存空间。这不如在x86-64上有用,因为在x86-64上,不使用它就很难创建arg数组
或者,您可以剥离处理$a1
的前3个求和迭代$a3
(再次假设寄存器中前4个参数的标准MIPS调用约定,$a0
为int n
)
然后,如果还没有执行n
,则循环堆栈参数
您可以编写一个C函数并查看优化的编译器输出,如下所示
#include <stdarg.h>
int sumargs(int n, ...) {
va_list args;
va_start(args, n);
int sum=0;
for (int i=0 ; i<n ; i++){
sum += va_arg(args, int);
}
va_end(args);
return sum;
}
在(--n)
时在do{}上循环会更有效。在编译for循环时,gcc没有这样做,这是一个遗漏的优化。在正常的MIPS调用约定中,第4个之后的arg将已经存储在调用堆栈上,由调用方放在那里
标准的调用约定在堆栈arg之前保留填充,您可以在其中存储寄存器arg以创建所有arg的连续数组,另见
这在x86-64 Windows中通常称为“阴影空间”。但由于MIPSjal
不将任何内容存储到内存中(不像x86在堆栈上推送返回地址,MIPS将返回地址放在$lr
中),即使调用约定不包括此阴影空间,函数仍然可以先调整SP,然后存储与堆栈参数相邻的寄存器参数。因此,我能看到的唯一好处是,在不必调整堆栈指针的情况下,为微小的函数提供额外的暂存空间。这不如在x86-64上有用,因为在x86-64上,不使用它就很难创建arg数组
或者,您可以剥离处理$a1
的前3个求和迭代$a3
(再次假设寄存器中前4个参数的标准MIPS调用约定,$a0
为int n
)
然后,如果还没有执行n
,则循环堆栈参数
您可以编写一个C函数并查看优化的编译器输出,如下所示
#include <stdarg.h>
int sumargs(int n, ...) {
va_list args;
va_start(args, n);
int sum=0;
for (int i=0 ; i<n ; i++){
sum += va_arg(args, int);
}
va_end(args);
return sum;
}
在(--n)
时在do{}上循环会更有效。gcc在编译for循环时没有这样做,这是一个遗漏的优化。你确定教授不是要你使用数组吗?您可以使用varadic函数来实现,但是如果调用约定在寄存器中传递第一个参数,并且堆栈中没有归位,那么实现需要一些预处理,否则会变得混乱。MIPS在寄存器中传递第一个参数,其余参数在堆栈中传递而不返回原点(我想),因此最好在注册到varadic实现之前先弄清楚n个数字是如何传递的。@MargaretBloom是的,他说它应该相当于C中的add(3,1,2,3),add(2,1,5)(第一个参数告诉我们后面会有多少个数字)@MargaretBloom:MIPS使用链接寄存器而不是推送返回地址,因此您可以将寄存器arg与堆栈arg连续存储以形成一个arg数组。事实上,它也会像Windows x64一样留下阴影空间。可能如此小的函数可以节省对堆栈指针的操作?例如,它们可以在那里存储$lr
在调用另一个函数之前?但是没有,他们必须为被调用方保留阴影空间,所以我认为这只对需要溢出某些内容的叶函数有帮助。看起来这是一个糟糕的设计(特别是与具有红色区域相比)。也许是一些放松的约定?@PeterCordes关于$lr
的观点很好。事实上,我并不期望MIPS有阴影/主空间。总之,这比预期的要容易。@MargaretBloom:是的,我也很惊讶地发现它有阴影/主空间,就像我在回答中说的。没有阴影/主空间,它已经很小了,与x64不同,它的价值要低得多(我认为红色区域会是更好的选择)。但即使没有红色区域,剥离前3次迭代也很容易。或者是一个单独的带有2个循环的数组,或者完全剥离/展开。你确定教授不是要你使用数组吗?Y