Функция с неопределенным числом аргументов MIPS - PullRequest
0 голосов
/ 18 января 2019

Я совершенно новичок в 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

Я пытался собратьэто как есть, но мой МАРС перестает отвечать.Любая подсказка, почему?

1 Ответ

0 голосов
/ 18 января 2019

В обычном соглашении о вызовах 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.

...