在電腦科學中,Call stack 是指存放某個程式的正在啟動並執行函數的資訊的棧。Call stack 由 stack frames 組成,每個 stack frame 對應於一個未完成啟動並執行函數。
在當今流行的電腦體系架構中,大部分電腦的參數傳遞,局部變數的分配和釋放都是通過操縱程式棧來實現的。棧用來傳遞函數參數,儲存傳回值資訊,儲存寄存器以供恢複調用前處理機狀態。每次調用一個函數,都要為該次調用的函數執行個體分配棧空間。為單個函數分配的那部分棧空間就叫做 stack frame,也就是說,stack frame 這個說法主要是為了描述函數調用關係的。
Stack frame 組織方式的重要性和作用體現在兩個方面:
第一,它使調用者和被調用者達成某種約定。這個約定定義了函數調用時函數參數的傳遞方式,函數傳回值的返回方式,寄存器如何在調用者和被調用者之間進行共用;
第二,它定義了被調用者如何使用它自己的 stack frame 來完成局部變數的儲存和使用。
描述的是一種典型的(MIPS O32)嵌入式晶片的 stack frame 組織方式。在這張圖中,電腦的棧空間採用的是向下增長的方式,SP(stack pointer) 就是當前函數的棧指標,它指向的是棧底的位置。Current Frame 所示即為當前函數(被調用者)的 frame ,Caller’s Frame 是當前函數的調用者的 frame 。每個 frame 中所存放的內容和存放順序,則由目標體系架構的呼叫慣例(calling convention)定義。,MIPS O32呼叫慣例規定了所佔空間不大於4 個位元的參數應該放在從 $4到 $8 的寄存器中,剩下的參數應該依次放到調用者 stack frame 的參數域中,並且在參數域中需要為前四個參數保留棧空間;如果被調用者需要使用 $16 到 $23 這些保留寄存器(saved register),就必須先將這些保留寄存器的值儲存在被調用者 stack frame 的保留寄存器域中,當被調用者返回時恢複這些寄存器值;當被調用者不是葉子函數時,即被調用者中存在對其它函數的調用,需要將 RA(return address) 寄存器 ($31) 值儲存到被調用者 stack frame 的傳回值域中;被調用者所需要使用的局部變數,應儲存在被調用者 stack frame 的本地變數域中。
在沒有 BP(base pointer) 寄存器的目標架構中,進入一個函數時需要將當前棧指標向下移動 n 位元,這個大小為n位元的儲存空間就是此函數的 stack frame 的儲存地區。此後棧指標便不再移動,只能在函數返回時再將棧指標加上這個位移量恢複棧現場。由於不能隨便移動棧指標,所以寄存器壓棧和出棧都必須指定位移量,這與 x86 架構的電腦對棧的使用方式有著明顯的不同。
在 RISC 電腦中主要參與計算的是寄存器,saved registers 就是指在進入一個函數後,如果某個儲存原函數資訊的寄存器會在當前函數中被使用,就應該將此寄存器儲存到堆棧上,當函數返回時恢複此寄存器值。而且由於 RISC 電腦大部分採用定長指令或者定變長指令,一般指令長度不會超過32個位。而現代電腦的記憶體位址範圍已經擴充到 32 位,這樣在一條指令裡就不足以包含有效記憶體位址,所以RISC電腦一般藉助於一個返回地址寄存器 RA(return address) 來實現函數的返回。幾乎在每個函數調用中都會使用到這個寄存器,所以在很多情況下 RA 寄存器會被儲存在堆棧上以避免被後面的函數調用修改,當函數需要返回時,從堆棧上取回 RA 然後跳轉。移動 SP 和儲存寄存器的動作一般處在函數的開頭,叫做 function prologue;恢複這些寄存器狀態的動作一般放在函數的最後,叫做 function epilogue。