В чем ошибка этого вызова процедуры MIPS? - PullRequest
1 голос
/ 20 февраля 2020

этот код MIPS содержит функцию int fun c (int a, int b), которая вызывает другую функцию foo (a, b).

func: addi $sp,$sp,-4    
      sw $ra, 0($sp)    
      addi $a0,$a0,1    
      addi $a1,$a1,2    
      jal foo
      addi $a0,$v0,4    
      add $v0,$a0,$a1    
      lw $ra,0($sp)    
      addi $sp,$sp,4    
      jr $ra

Этот код содержит некоторые ошибки, но я не не знаю, где не так.

1 Ответ

2 голосов
/ 20 февраля 2020

Самым серьезным нарушением является то, что func ожидает, что $a1 переживет вызов функции до foo - что неверно. По соглашению о вызовах $a1 является регистром аргументов и не сохраняется при вызове функции. $a1 также не является возвращаемым значением, поэтому его следует считать неинициализированным после вызова функции, т. Е. Оно содержит ненужный мусор.

func: addi $sp,$sp,-4    
      sw $ra, 0($sp)    
      addi $a0,$a0,1    
      addi $a1,$a1,2    
      jal foo           # this call effectively wipes out argument registers (ok)
      addi $a0,$v0,4    # here the function re-initializes $a0 from return value $v0 (ok)
      add $v0,$a0,$a1   # but here it uses uninitliazed $a1 (not ok)
      lw $ra,0($sp)    
      addi $sp,$sp,4    
      jr $ra

Правильный способ сохранить аргумент func s $a1 сохранить его в памяти и загрузить позже, после вызова функции; для этого потребуется дополнительное слово в кадре стека.

func: addiu $sp,$sp,-8    
      sw $ra, 4($sp)    
      addi $a0,$a0,1    
      addi $a1,$a1,2 
      sw $a1, 0($sp)    # save a1 in stack before call
      jal foo           # this call effectively wipes out argument registers
      lw $a1, 0($sp)    # restore a1 from stack after call
      addi $a0,$v0,4    # here the function re-initializes $a0 from return value $v0
      add $v0,$a0,$a1   # now using re-initialized $a1 (ok)
      lw $ra,4($sp)    
      addiu $sp,$sp,8    
      jr $ra

В приведенном выше примере я сохранил обновленный регистр $a1, но, разумеется, из простого чтения кода не ясно действительно ли желателен исходный, без приращения $a1 или обновленный $a1. Похоже, что $a1 передается в качестве параметра в foo, поэтому может быть, что foo хочет увеличить значение, но позже func не делает.

В качестве альтернативы можно сохранить $a1 в регистре $s, поскольку они гарантированно сохраняются в вызове функции соглашением о вызовах - однако, согласно этому самому определению, $s сами регистры, если они используются вызываемым объектом, должны быть сохранены и восстановлено, поэтому все еще потребуется дополнительное слово в кадре стека.


Другие нарушения соглашения о вызовах для MIPS заключаются в следующем:

  • MIPS CC требует, чтобы стековые кадры были выровнены на 8 байт, поэтому даже если вам нужно только одно слово, мы должны округлить размер стекового кадра до кратного 8 байт. Многие программисты на ассемблере игнорируют это, однако, без серьезных последствий.

  • MIPS CC также диктует место для 4 регистров аргументов, которые должны быть сохранены в стеке, и что это пространство обеспечивается вызывающий, чтобы вызываемый мог использовать его (не требуя какого-либо выделения стека вызывающим). Это почти никогда не делается программистами на ассемблере в простых заданиях, хотя это технически необходимо. Если вызываемый пользуется этими 4 словами, которые должны быть там, когда их нет, произойдут плохие вещи. (Я не учел это в приведенном выше решении.) Я бы минимально следовал этому требованию при написании функции, которая вызывает функцию varargs , например printf, sprintf, scanf и т. Д. c. Эта область сохранения 4 регистра аргументов соприкасаются с памятью для 5-го, 6-го и т. д. c .. аргументов, что делает весь блок параметров непрерывным в памяти, что полезно при реализации функций varargs.

https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf


Кроме того, addi, используемый для выделения и освобождения стекового пространства, лучше, чем addiu - поскольку это арифметика указателей c (адреса не подписаны), целочисленное переполнение со знаком в лучшем случае неинтересно, а в худшем - вредно (вызывая неуместное исключение переполнения).

...