2 1 RISC V::關於基本暫存器 - ianchen0119/AwesomeCS GitHub Wiki
本文目標
- 認識暫存器
- 建立 Callee save 與 Caller save 的觀念
- 了解呼叫慣例
進入正題
下圖列舉了 RISC-V 處理器中的通用暫存器:
針對上圖,筆者針對一些可能會有疑問的點進行補充:
-
x0 暫存器
x0 暫存器的數值為 0 且無法更動,我們可以利用他做到類似 mov 的作用:
add a0 a1, x0 # a0 = a1
-
Return Address
Return Address 存放函式執行結束時返回的位置,假設,有兩個函式 a, b :
main() -> a() a() -> b()
因為
main()
呼叫了函式a()
,在跳進函式a()
執行前,main()
會將ra
暫存器的值放到 Stack 上。 此時,如果函式a()
又呼叫了函式b()
,在呼叫之前我們會將函式a()
的 return address 儲存到 Stack,避免 ra 暫存器被函式b()
複寫後無法返回到main()
繼續執行。由此可知,ra
由呼叫者維護暫存器的狀態,這類特性的暫存器都是 Caller save。 在這邊如果不考慮 Stack pointer,Stack 內部看起來會是:+------------------------------+ | Data stored by function b | +------------------------------+ | Return Address of function a | +------------------------------+ | Return Address of main | --+------------------------------+--
-
Stack pointer
然而,每個 Process 都會有屬於自己的區域變數,這些變數同樣會被存放在 Stack 中,Stack pointer 會指向 Stack 中屬於此 Process 的位址。 不過,這部分與
ra
會些不同,sp
暫存器被定義為 Callee save,當被呼叫的函式修改sp
暫存器的內容之前,必須先將內容保存下來,並在函式返回前恢復原本的值。 如果以main()
呼叫function a()
來看,流程如下:
main() 將當前 ra 的值放到 stack -> 進入 function a() -> 將 sp 的值存放到 stack -> 移動 sp 的位址 (Scope of function a) -> 一些運算 -> 返回到 main 之前恢復 sp 的值 -> 跳回 main() -> 恢復 main() 的 ra
-
Function args: a0-a7 (x10-x17)
a0-a7 共 8 個暫存器可供我們用於存取函式的參數值,當我們呼叫函式時,函式的參數會依序的存放到這些暫存器中。
-
Return values: a0-a1
a0, a1 暫存器除了會被當作 args 使用,當函式需要有 return value 時,我們可以將 return value 存放至 a0 與 a1 暫存器,供其他函式取得返回值。
總結
本篇文章介紹了 RISC-V 之中常用的暫存器,包含了:
- 每個暫存器的特殊用途
- 呼叫函式的前後該如何保存各個暫存器的內容
以上兩點其實就是 RISC-V 的 Calling convention (呼叫慣例),這些規範在編譯器的實作上都能看到,理解它更有助於嵌入式的系統軟體開發。