Skip to content

Commit

Permalink
術的施展
Browse files Browse the repository at this point in the history
  • Loading branch information
MROS committed Oct 15, 2024
1 parent 588043d commit 1425e5f
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 12 deletions.
8 changes: 6 additions & 2 deletions book/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,12 @@ export default defineConfig({
link: "/零.二版/語義分析:類型檢查.md",
},
{
text: "精五真言生成(一)施術",
link: "/零.二版/精五真言生成(一)施術.md",
text: "精五真言生成(一)棧與區域變數",
link: "/零.二版/精五真言生成(一)棧與區域變數.md",
},
{
text: "精五真言生成(二)術的施展",
link: "/零.二版/精五真言生成(二)術的施展.md",
},
],
},
Expand Down
Binary file added book/image/參數精五棧圖解.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 8 additions & 4 deletions book/用語對照.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@


## 零.二版引入

##
###
函式

### 外術
外部函式

### 施者
caller

### 受者
callee

## 符號
### 基括號
〖〗
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

但零.二版引入了術之後,一切都變了,術內宣告的變數都是區域變數;而程式碼在施術後會跳到另一個術裡執行,完成後又回到原本的術裡。

## 棧與術
## 區域變數

由於在咒執行的過程中,一個術可能被施展多次,最簡單的例子就是遞迴術,術中有術,層層嵌套。由於執行次數可能取決於法咒的輸入,編譯器無法知曉究竟一個術在法咒執行過程中究竟會執行幾次,因此不可能在編譯期就將記憶體分配完
咒執行的過程中,一個術可能被施展多次,最簡單的例子就是遞迴術,術中有術,層層嵌套。由於執行次數可能取決於法咒的輸入,編譯器無法知曉究竟一個術在法咒執行過程中究竟會執行幾次,因此不可能在編譯期就將樹中區域變數的記憶體分配完

那只好在執行期動態分配記憶體了,分配到哪裡呢?[老樣子](../零.一版/精五真言生成.md),放到棧上,畢竟術的施展天然就像棧這種資料結構。

Expand Down Expand Up @@ -41,14 +41,14 @@

當計算機執行術時,會將計算機的咒指針(program counter)指向術的開頭,隨著咒指針遞增,術就一行行執行下去了,當甲術執行到要施展乙術後,乙必須先記錄當下的施者(caller)——也就是甲——施術時的咒指針,在乙術結束時,才能夠再跳回甲施乙術後的下一條真言執行。

### 臨時變數
## 臨時變數
注意到``術的最後一行 `曰(宇+宙+洪+荒)` ,回憶在[零.一版](../零.一版/精五真言生成.md)中,計算算式時,吾人會將計算機當成一個堆疊機來用,在棧上開出更多空間以存放計算的中間結果。

這就意味著單純採用堆疊機,且不進行優化,是沒法僅在棧中開四個變數的空間就完成計算。

這有幾種不同的作法:

#### 編譯器計算出臨時變數需要的空間
### 編譯器計算出臨時變數需要的空間
例如,編譯器能夠直接計算出需要幾個臨時變數,例如可以將原``術在中間碼就轉換成如下邏輯

```
Expand All @@ -68,7 +68,7 @@
```
則可以配出七個整數的空間。(此非最優解)

#### 棧動態增長
### 棧動態增長
也可以如零.一版一樣,一邊計算一邊把臨時結果壓入棧,實作可能簡單一些。但最終仍要把棧恢復到舊貌,也就是說仍然要記錄棧到底增長了多少,或者採用 `fp` 暫存器來記錄當下的棧底為多少。

臨時增長棧也會導致用 `sp` 來索引區域變數的位址會不太穩定,以 `fp` 來索引實作會比較容易。(棧臨時增長時,`sp`動,但`fp`不動)
Expand Down Expand Up @@ -194,7 +194,6 @@ gcc 可以用 `-fomit-frame-pointer` 來調控是否採用 `fp` 的棧形式,`
ret # jr ra
```
全在暫存器就算完了。

再來看看乙那段將宇、宙、洪、荒相加的算式,若編譯器好好優化,會怎麽計算:

Expand Down
94 changes: 94 additions & 0 deletions book/零.二版/精五真言生成(二)術的施展.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
在上一章討論了區域變數如何存放在棧中,在[零.一版](../零.一版/精五真言生成.md)中提及了全域變數如何生成。現在,還剩下一種變數沒有討論——**參數**

## 精五真言施術約定
精五有 32 個通用暫存器,施術時 `call 術` 這個偽指令做的不過是將咒指針(program counter)跳躍到``所在位址,把就位址放到 `ra` 暫存器而已。

施者(caller)如何傳遞資訊給受者(callee)呢?其實怎樣都可以,無論是丟到棧上的某個位置還是丟到特定暫存器都可以。總之**講好就好**。如果不同編譯器不按照同個約定來傳遞參數,那這些編譯器編譯出來的術就無法互相調用了。

精五的[標準施展約定](https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf)規定在整數情況下,使用 `a0` ~ `a7` 暫存器來傳遞變數。這些暫存器吾人稱之為參數暫存器。(a 即是 argument 的縮寫)

而術結束後,會將其歸值(return value)放在 `a0``a1`,歸值若僅一個字長,只要看 `a0` 就好了。

貧道用 gcc 編譯個簡單的 C 程式(音界咒當前版本也依賴類似外術),來看看是否按照這個約定就能呼叫到它了。

## 編譯外術(外部函式)

將以下 C 代碼存入 `曰.c` 檔案中。

```c
#include <stdio.h>
#include <inttypes.h>

int64_t print_int(int64_t number) {
printf("%" PRId64 "\n", number);
return 111;
}
```
執行以下指令來編譯該檔案
```sh
riscv64-unknown-elf-gcc -c 曰.c -o 曰.o
riscv64-unknown-elf-objcopy --redefine-sym print_int=曰 曰.o
```

C 語言不支援非 ASCII 的字符,得先編譯出目的檔 `曰.o` 之後,再將其符號從 `print_int` 抽換成 ``

## 施術範例

再將以下檔案存到 `施展.S`

```assembly
.section .text
.global main
main:
li a0, 17
call 曰
call 曰
li a7, 93 # ecall號碼93表示退出
li a0, 0
ecall
```

用以下指令組譯 `施展.S` 並鏈結`曰.o`
```
riscv64-unknown-elf-gcc 施展.S 曰.o
```
再以 qemu 執行生成的執行檔 `a.out`

```
qemu-riscv64 a.out
```
會看到終端輸出
```
17
111
```
第一行 `17` 正是吾人放進 `a0` 的值,而第二行的 `111` 恰好就是曰的歸值,第一個 `call 曰` 結束之後,`a0` 已經被改為 `111` ,立刻再施展一次 `` ,果然輸出了 `111`

看來 gcc 確實按照這個約定在傳遞參數與回傳值,音界咒編譯器也遵照約定,以在未能完全自舉之前方便與塵界之術互動。

## 儲存參數

既然在術開始之前,參數就已被施者填入 `a0` ~ `a7` 之中,那能否每次需要讀取參數時就直接使用暫存器呢?不能,因為術有可能還要去調用其他術,這是又得修改 `a0` ~ `a7` 來傳遞參數了。

還是只能老方法全都存到棧裡了。

如果不需要再調用其他術,大可以就放在參數暫存器裡,但零.二版音界咒的實作先不做這個優化。

來看個範例:

```音界
術.甲(子、丑)【
元.天=1
元.地=1
元.玄=1
元.黃=1
```

執行後,棧應該要是

![加入參數的精五棧圖解](../image/參數精五棧圖解.png)

0 comments on commit 1425e5f

Please sign in to comment.