[英]Understanding recursion with MIPS assembly language
我在上課,我們正在/正在討論匯編語言的遞歸。 我覺得自己了解遞歸,但是越來越多的人嘗試向我解釋遞歸,我與遞歸的距離就越遠。
無論如何,我們的最后一張幻燈片有一個功能(用C語言編寫),老師說他將在課堂上介紹它,但請我們的學生在黑板上展示其余課程。 我覺得他一直在看着我,對自己看起來很愚蠢感到非常恐懼。
你們可以幫我用MIPS編寫此代碼並幫助我理解它嗎? IDK,如果這太難了
用MIPS匯編語言編寫以查找fix(i,x),其中fix(i,x)遞歸定義為:
int fix(int i, int x) // assume i > 0, x > 0
{
if (x>0)
return fix(i,x-1);
else if (i>0)
return fix(i-1, i-1)+1;
else
return 0;
}
謝謝大家,我明天上課,我仍然希望他永遠不要拜訪我。 但我想真正地理解這些材料。
注意:這將是一個課堂練習,附帶0個學分。 我覺得班上的每個人都已經知道該怎么做。
匯編語言與遞歸無關,只是由於C語言以及調用約定和實現而起作用。 只需在匯編器中實現C,而不用擔心遞歸。 我想我在一個寵物項目http://github.com/dwelch67/lsasim上涉及到了這一點,除非我將其更改為上一課是將C中的遞歸手動轉換為匯編器。 它不是mi花一現,因此不必擔心這是一個家庭作業問題。
無論如何,啟動的關鍵是簡單地在匯編中實現C。
例如,您有一個帶有輸入參數的函數。
int fix(int i, int x)
您需要聲明自己的調用約定或使用現有的調用約定來實現C,這意味着您需要為輸入參數留出一定的位置,或者將它們壓入堆棧或將其放入寄存器中。 假設沒有優化,則可以在整個函數中保留這些變量,並在最后進行清理。 因此,如果您需要在代碼中的任何地方調用ANY函數(遞歸,調用相同的函數,是ANY的很小的子集,但屬於該類別且不是特殊的),則需要保留這些變量。 如果調用約定將它們引入堆棧中,那么您已經完成了,如果調用約定將它們引入寄存器中,則需要在調用之前保留它們,並在調用之后恢復
push i
push x
implement call to function
pop x
pop i
並繼續執行該功能。
就是這樣,其余的會照顧好自己。
如果您偶然發現作為示例創建的函數,則沒有在該函數內調用函數后需要保留輸入變量的路徑。 輸入變量將被修改並用作下一個調用的輸入。 因此,對C代碼實現的優化將不必擔心保留這些變量。 只需對其進行修改並將其繼續下去。 對於特定功能,在調用約定中使用寄存器是最簡單的方法。 這是編譯器在優化時仍會做的事情(如果使用基於寄存器的調用約定,則不會保留)。
如果這就是所謂的尾巴優化,也可以進行。 通常,在調用函數時,您可以使用指令通常執行的“調用”來執行“調用”,該操作不同於簡單的跳轉或分支,因為在某處保留有返回值。 還有某種返回函數可以取消此操作,並在調用后返回到指令。 嵌套調用意味着嵌套返回值,並跟蹤所有返回值。 但是,在這種情況下以及在其他情況下,您執行功能執行路徑的最后一件事是調用另一個功能,則可以改為(取決於指令集)分支到該功能,而不必嵌套另一組返回值。 以arm指令集為例:
一些代碼調用第一個函數:
bl firstfun:
在臂bl中表示分支鏈接。 寄存器14將用返回值填充,程序計數器將用函數firstfun的地址填充。
通常,如果您需要從一個函數中調用一個函數,則需要保存r14以便可以從該函數返回,而無需進行尾部優化:
firstfun:
...
push {r14}
bl secondfun
pop {r14}
bx r14
...
secondfun:
bx r14
bx lr僅表示分支到r14中的內容,在這種情況下為返回。 優化看起來像這樣,需要注意的是,在第一個函數中調用第二個函數是您從第一個函數返回之前要做的最后一件事。 這是優化的關鍵。
firstfun:
...
b secondfun
...
secondfun:
bx r14
b僅表示分支,不是分支鏈接僅修改pc且不修改r14或任何其他寄存器或堆棧。 這兩個實現的執行在功能上是相同的,外部函數對firstfun進行了調用,並且正確的執行路徑中有一個返回(bx r14)。
其他人指出,由於您將原始調用者返回零,因此該代碼可能完全將自身優化為空。
fix:
return 0
盡管我反對僅僅給出作業問題的答案,但是這里是等效的函數,可以找到fix(i, x)
,並帶有示例調用(此版本比C版本更有效):
fix:
bgtz RetI
xor $v0, $v0, $v0
jr $ra
RetI:
move $v0, $a0
jr $ra
# example of calling fix
main:
la $a0, 42
la $a1, 21
jal fix
並為您提供一堂課,以了解在嘗試對函數進行編碼之前的功能:)
像上面的C一樣將其分成3部分。 通過寄存器傳遞值i和x,然后在運行if檢查並修改寄存器后調用自己。 上面的匯編程序行不應該超過30行。 如果我是一個更好的MIPS編碼器,我會為您做。 它看起來像(是psuedo匯編程序)
fix:
compare r0, 0
branch greater than x_positive
subtract r1,r1,1
call fix
return;
// or just jump fix instead
x_positive:
compare r1, 0
branch greater than i_positive
subtract r0, r0, 1
subtract r1, r1, 1
call fix
return
// or just jmp fix
i_positive:
move return register, 0
return
有趣的是,它將始終返回用C編寫的0 :)
fix:
move return_register, 0
return
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.