File tree Expand file tree Collapse file tree 5 files changed +94
-1
lines changed Expand file tree Collapse file tree 5 files changed +94
-1
lines changed Original file line number Diff line number Diff line change @@ -87,6 +87,10 @@ export default defineConfig({
8787 text : "語義分析:類型檢查" ,
8888 link : "/零.二版/語義分析:類型檢查.md" ,
8989 } ,
90+ {
91+ text : "精五真言生成(一)施術" ,
92+ link : "/零.二版/精五真言生成(一)施術.md" ,
93+ } ,
9094 ] ,
9195 } ,
9296 {
Original file line number Diff line number Diff line change 1+ 執行檔所定義的事情,不外乎** 數據如何存放** 與** 程式如何執行** 。
2+
3+ 在零.一版中,所有的變數都是全域變數,程式執行也是一行一行往下。
4+
5+ 但零.二版引入了術之後,一切都變了,術內宣告的變數都是區域變數;而程式碼在施術後會跳到另一個術裡執行,完成後又回到原本的術裡。
6+
7+ ## 棧與術
8+
9+ 由於在咒執行的過程中,一個術可能被施展多次,最簡單的例子就是遞迴術,術中有術,層層嵌套。由於執行次數可能取決於法咒的輸入,編譯器無法知曉究竟一個術在法咒執行過程中究竟會執行幾次,因此不可能在編譯期就將記憶體分配完。
10+
11+ 那只好在執行期動態分配記憶體了,分配到哪裡呢?[ 老樣子] ( ../零.一版/精五真言生成.md ) ,放到棧上,畢竟術的施展天然就像棧這種資料結構。
12+
13+ ``` 音界
14+ 術.甲()【
15+
16+ 元.天=1
17+ 元.地=1
18+ 元.玄=1
19+ 元.黃=1
20+
21+ 乙()
22+ 】
23+ 術.乙()【
24+
25+ 元.宇=1
26+ 元.宙=1
27+ 元.洪=1
28+ 元.荒=1
29+
30+ 曰(宇+宙+洪+荒) // 曰是外術(外部函式)
31+ 】
32+ ```
33+
34+ 以下分別圖示剛施展甲術,以及甲術呼叫乙術之後的棧的樣子。
35+
36+ 圖左為剛施展甲術,未施展乙術;圖右為甲術施展乙術後:
37+
38+ ![ 精五棧圖解] ( ../image/精五棧圖解.png )
39+
40+ 圖中的區域變數對應甲、乙術中宣告的變數,很直觀能理解。但每個術在棧中還額外佔用了一塊空間「返回位址」,這是做什麼的呢?
41+
42+ 當計算機執行術時,會將計算機的咒指針(program counter)指向術的開頭,隨著咒指針遞增,術就一行行執行下去了,當甲術執行到要施展乙術後,乙必須先記錄當下的施者(caller)——也就是甲——施術時的咒指針,在乙術結束時,才能夠再跳回甲施乙術後的下一條真言執行。
43+
44+ ### 臨時變數
45+ 注意到` 乙 ` 術的最後一行 ` 曰(宇+宙+洪+荒) ` ,回憶在[ 零.一版] ( ../零.一版/精五真言生成.md ) 中,計算算式時,吾人會將計算機當成一個堆疊機來用,在棧上開出更多空間以存放計算的中間結果。
46+
47+ 這就意味著單純採用堆疊機,且不進行優化,是沒法僅在棧中開四個變數的空間就完成計算。
48+
49+ 這有幾種不同的作法:
50+
51+ #### 編譯器計算出臨時變數需要的空間
52+ 例如,編譯器能夠直接計算出需要幾個臨時變數,例如可以將原` 乙 ` 術在中間碼就轉換成如下邏輯
53+
54+ ```
55+ 術.乙()【
56+
57+ 元.宇=1
58+ 元.宙=1
59+ 元.洪=1
60+ 元.荒=1
61+
62+ 元.和1=宇+宙
63+ 元.和2=洪+荒
64+ 元.和=和1+和2
65+
66+ 曰(和)
67+ 】
68+ ```
69+ 則可以配出七個整數的空間。(此非最優解)
70+
71+ #### 棧動態增長
72+ 也可以如零.一版一樣,一邊計算一邊把臨時結果壓入棧,實作可能簡單一些。但最終仍要把棧恢復到舊貌,也就是說仍然要記錄棧到底增長了多少,或者採用 ` fp ` 暫存器來記錄當下的棧底為多少。
73+
74+ 使用 ` fp ` 的棧會形如:
75+
76+ ![ 精五棧圖解] ( ../image/精五棧圖解fp.png )
77+
78+ 但無論如何,臨時增長棧就意味著修改 ` sp ` ,在執行期改暫存器會造成額外開銷,因此在實務中不太會採用這種策略。
79+
80+ gcc 可以用 ` -fomit-frame-pointer ` 來調控是否採用 ` fp ` 的棧形式,` fp ` 在棧中構成一個鏈結串列,故採用 ` fp ` 能輕鬆的追蹤施術鏈(函式調用鏈)。
81+
82+ 方便除錯也是設計 ` fp ` 的主要目的,只是剛好用了它能讓實作棧動態增長變得容易些罷了。
83+
84+ 不過要計算臨時變數也挺麻煩,先以記錄 ` fp ` 的方式來實作施術吧。
85+
86+ ## 術的真言形式
87+
88+ 來看看上述的甲、乙兩術翻成真言會是什麼樣子:
89+
Original file line number Diff line number Diff line change 132132
133133## 外術(外部函式)
134134
135- 支援 ` 打印整數 (數)` 此一外術,其接受一個整數作為參數,執行後會將該整數送進標準輸出。
135+ 支援 ` 曰 (數)` 此一外術,其接受一個整數作為參數,執行後會將該整數送進標準輸出。
136136
137137## 法咒執行流程
138138
You can’t perform that action at this time.
0 commit comments