---
# System prepended metadata

title: RISC-V Procedures
tags: [RISC-V, Computer Architecture]

---

# RISC-V Procedures
> 資料來源：NCKU Jserv 教授開設之**計算機結構**課程－[RISC-V Procedures](https://docs.google.com/presentation/d/18F1vHY5Mv5E6WU6BvktXzkVbZrTZ3jQUbkWOjEPGE4I/edit#slide=id.p1)。

:::info
可回來查閱 **RISC-V Symbolic Register Names**。
:::

## RISC-V Function Call

### Six Fundamental Steps in Calling a Function
1. 將 **arguments** 放在函式可以存取的位置。
2. 將「控制權」轉交給函式。
3. 取得函式所需的（本地）儲存資源。
4. 執行函式所需的任務。
5. 將 **return value** 放在呼叫程式碼可以存取的位置，並恢復使用過的暫存器；釋放本地儲存資源。
6. 將「控制權」交回給原點，因為一個函式可能從程式的多個點被呼叫。

### RISC-V Function Call Conventions
- 暫存器比記憶體快，因此應善加利用它們。
- `a0` – `a7` (`x10` - `x17`)：**8 個參數暫存器**，用來傳遞參數及兩個 return value（`a0` - `a1`）。
- `ra`：**1 個 return address 暫存器**，用來回到到原點（`x1`）。
- 另外還有 `s0`-`s1` (`x8`-`x9`) 和 `s2`-`s11` (`x18`-`x27`)： saved 暫存器（稍後會進一步解釋）。

### RISC-V Function Call Instructions
- **呼叫函式**：使用 jump and link 指令（`jal`）。
    - 「link」的意思是建立一個位址或連結，指向呼叫的地方，讓函式能夠返回到正確的位址。
    - 「jump」到指定位址，並同時將下一條指令的位址儲存到 `rd` 暫存器中：
        ```c
        jal rd, FunctionLabel
        ```
- 從**函式回傳**：使用 jump register 指令（`jr`）。
    - 無條件跳躍到 `ra` 暫存器指定的位址：
        ```c
        jr ra
        ```  
    - 組譯器的簡寫：`ret = jr ra`。

小總結：

事實上，只有兩個指令：
- `jal  rd, Label` – **jump-and-link** `# rd = pc+4; pc += imm`
- `jalr rd, rs, imm` – **jump-and-link register** `# rd = pc+4; pc = R[rs1]+imm`
    - 正如我們即將看到的，「天下沒有白吃的午餐」，因此可能**沒有足夠的位元**讓 Label 指向我們想跳躍的遠處。
    - 使用 `jalr` 時，我們跳躍到暫存器 `rs` 的內容加上立即值 `imm`（就像基底指標加上偏移量），並且將 `rd` 設置為和 `jal` 相同的方式。

> 注意：`j`, `jr` 和 `ret` 是 pseudo-instructions!

### Stack

> 在呼叫函數之前，需要一個地方來**保存舊的值**，返回時還原它們，並刪除這些值。

理想的選擇是 **Stack**：一種 LIFO 的佇列。
- Push：將資料放入 Stack。
- Pop：從 Stack 中取出資料。

Stack 位於記憶體中，因此需要一個暫存器指向它。
RISC-V 中的 Stack Pointer 為 `sp`（`x2`）。
慣例上，Stack 從高位址向低位址增長，執行 Push 時減少 `sp`，執行 Pop 時增加 `sp`。

Stack **frame** 包含：
- Return「instruction」位址
- 參數（引數）
- 其他區域變數的空間

stack frame 是連續的記憶體區塊， Stack Pointer 會**指示 stack frame 的底部位置**。
![圖片](https://hackmd.io/_uploads/S1BzP6ekJe.png =30%x)

當程式執行完畢後，stack frame 會從 Stack 中移除，釋放記憶體以供未來的 stack frame 使用。

## RISC-V Function Call Example

### C Code
```c
int Leaf (int g, int h, int i, int j)
{
	int f;
	f = (g + h) – (i + j);
	return f;
}
```
> Parameter variables `g`, `h`, `i`, and `j` in argument registers `a0`, `a1`, `a2`, and `a3`, and `f` in `s0`.
Assume need one temporary register `s1`.

### Assembly Code
```c
Leaf: 	addi sp,sp,-8  # adjust stack for 2 items
	sw s1, 4(sp)   # save s1 for use afterwards
      	sw s0, 0(sp)   # save s0 for use afterwards

      	add s0,a0,a1   # f = g + h
      	add s1,a2,a3   # s1 = i + j
      	sub a0,s0,s1   # return value (g + h) – (i + j)

      	lw s0, 0(sp)   # restore register s0 for caller 
      	lw s1, 4(sp)   # restore register s1 for caller
      	addi sp,sp,8   # adjust stack to delete 2 items
      	jr ra 	       # jump back to calling routine
```

### Stack Before, During, After Function
![圖片](https://hackmd.io/_uploads/rkf3Oae1yx.png =80%x)


## Nested Calls and Register Conventions

### Nested Procedures
```c
int sumSquare(int x, int y) {
    return mult(x, x) + y;
}
```
有個名為 `sumSquare` 的函式，現在 `sumSquare` 呼叫了 `mult` 函式。
此時，`sumSquare` 需要 return 的 addr. 儲存在 `ra` 中，但在呼叫 `mult` 時，`ra` 會被覆寫。

因此，在呼叫 `mult` 之前，必須**先將 `sumSquare` 的 return address 存放到 Stack 中**，以避免被覆寫。

### Register Conventions
- 呼叫者（Calle**R**）：指發起呼叫的函式。
- 被呼叫者（Calle**E**）：指被呼叫的函式。

> 當 Calle**E** 執行完畢 return 時，Calle**R** 需要知道哪些暫存器可能會被改變，哪些則保證不會被改變。

**暫存器慣例（Register Conventions）**：這是一組普遍接受的規則，說明在執行函式呼叫（`jal`）後，哪些暫存器會保持不變，哪些可能會被改變。

為了減少因暫存器溢出與恢復所造成的昂貴 load 和 store 操作，RISC-V 的 function-calling convention 將暫存器分為兩類：
1. 函式呼叫後，**保留的暫存器**：
   - 呼叫者可以依賴其值不變。
   - `sp`（堆疊指標）、`gp`（全域指標）、`tp`（執行緒指標）。
   - Saved registers：`s0 - s11`（`s0` 也作為 `fp`，即框架指標），
2. 函式呼叫後，**不保留的暫存器**：
   - 呼叫者無法依賴其值保持不變。
   - Argument / Return registers：`a0 - a7`、`ra`（返回位址暫存器）。
   - Temporary registers：`t0 - t6`。

### RISC-V Symbolic Register Names

![圖片](https://hackmd.io/_uploads/rJlusagkJe.png =90%x)
> **Register**：Numbers hardware understands。
> **ABI Name**：Human-friendly symbolic names in assembly code.

## RV32 指令
![圖片](https://hackmd.io/_uploads/B1sknaeJ1e.png =70%x)
