我对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
我尝试按原样组装它,但我的MARS停止响应。有什么线索吗?
答案 0 :(得分:4)
在正常的MIPS调用约定中,第4位之后的args将已存储在调用堆栈中,由调用方放置在该堆栈中。
标准调用约定在堆栈args之前保留填充,您可以在其中存储寄存器args以创建所有arg的连续数组。 This PDF has a diagram,另请参阅MIPS function call with more than four arguments
这在x86-64 Windows中通常称为“影子空间”。但是,由于MIPS jal
不会将任何内容存储到内存中(与x86会将返回地址压入堆栈的情况不同,MIPS会将返回地址放入$lr
中),即使调用约定不包括此内容也是如此。影子空间函数仍然可以先调整SP,然后再存储与堆栈arg相邻的寄存器args。因此,我唯一能看到的好处就是为微型函数提供了额外的暂存空间,而无需调整堆栈指针。这比在x86-64上没有用处要好,在x86-64上,没有它就很难创建args数组。
或者您也可以剥离处理$a1
.. $a3
的前3个总和迭代(同样假设标准MIPS调用约定具有寄存器中的前4个arg,$a0
为{{ 1}})。
如果尚未进入int n
,则循环遍历堆栈args。
您可以编写一个C函数,然后查看经过优化的编译器输出,如下所示
n
#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;
}
和va_start
不是真正的函数;他们将扩展为一些内联代码。 va_arg
将va_start(args,n)
之后的arg传递寄存器转储到影子空间(与堆栈args,如果有的话)。
不幸的是,MIPS gcc不支持n
选项来使用$ a0和$ t0之类的名称,但是Google发现a nice table of register name<->number
MIPS asm output from the Godbolt compiler explorer
-mregnames
在# gcc5.4 -O3 -fno-delayed-branch
sumargs(int, ...):
# on entry: SP points 16 bytes below the first non-register arg, if there is one.
addiu $sp,$sp,-16 # reserve another 16 bytes
addiu $3,$sp,20 # create a pointer to the base of this array
sw $5,20($sp) # dump $a1..$a3 into the shadow space
sw $6,24($sp)
sw $7,28($sp)
sw $3,8($sp) # spill the pointer into scratch space for some reason?
blez $4,$L4 # check if the loop should run 0 times.
nop # branch-delay slot. (MARS can simulate a MIPS without delayed branches, so I told gcc to fill the slots with nops)
move $5,$0 # i=0
move $2,$0 # $v0 = sum = 0
$L3: # do {
lw $6,0($3)
addiu $5,$5,1 # i++
addu $2,$2,$6 # sum += *arg_pointer
addiu $3,$3,4 # arg_pointer++ (4 bytes)
bne $4,$5,$L3 # } while(i != n)
nop # fill the branch-delay slot
$L2:
addiu $sp,$sp,16
j $31 # return (with sum in $v0)
nop
$L4:
move $2,$0 # return 0
b $L2
nop
上循环会更有效。这是一个错过的优化,gcc在编译for循环时不会执行此操作。