В обычном соглашении о вызовах MIPS, аргументы после 4-го уже будут храниться в стеке вызовов, размещенном там вашим вызывающим абонентом.
Стандартное соглашение о вызовах оставляет заполнение до аргументов стека, где вы можете сохранитьзарегистрируйте аргументы, чтобы создать непрерывный массив всех аргументов. Этот PDF-файл содержит диаграмму , а также см. Вызов функции MIPS с более чем четырьмя аргументами
Обычно это называется "теневым пространством" в Windows x86-64.Но поскольку MIPS jal
ничего не сохраняет в памяти (в отличие от x86, который помещает адрес возврата в стек, MIPS помещает адрес возврата в $lr
), даже если соглашение о вызовах не включает это теневое пространство в функциювсе еще можно сначала настроить SP, а затем сохранить аргументы регистров, смежные с аргументами стека.Таким образом, единственное преимущество, которое я могу видеть, это дать крошечным функциям дополнительное пространство для нуля без необходимости настройки указателя стека.Это менее полезно, чем на x86-64, где без него невозможно создать массив аргументов.
Или вы можете очистить первые 3 итерации суммы, которые обрабатывают $a1
.. $a3
(опять-таки, предполагая стандартное соглашение о вызовах MIPS с первыми 4 аргументами в регистрах, $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;
}
va_start
и va_arg
не являются реальными функциями;они расширятся до некоторого встроенного кода.va_start(args,n)
сбрасывает регистры передачи аргументов после n
в теневое пространство (смежное с аргументами стека, если оно есть).
MIPS gcc, к сожалению, не поддерживает опцию -mregnames
для использования имен, таких как $a0 и $ t0, но гугл нашел красивую таблицу с именем регистра <-> число
вывод MIPS asm из проводника компилятора Godbolt
# 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
Цикл на do {}while(--n)
был бы более эффективным.Оптимизация пропущена, поскольку gcc этого не делает при компиляции цикла for.