参数不确定的函数MIPS

时间:2019-01-18 09:47:20

标签: 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

我尝试按原样组装它,但我的MARS停止响应。有什么线索吗?

1 个答案:

答案 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_argva_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循环时不会执行此操作。