Skip to content

Commit a272c8f

Browse files
committed
介紹堆疊機
1 parent d741589 commit a272c8f

1 file changed

Lines changed: 107 additions & 2 deletions

File tree

book/零.一版/精五真言生成.md

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
### 精五真言的數據段
99

10-
很容易就能用精五真言設定全域變數,一下是範例`數據段.S`
10+
很容易就能用精五真言設定全域變數,以下是範例`數據段.S`
1111

1212
```assembly
1313
.section .data
@@ -32,7 +32,7 @@ qemu-riscv64 a.out # qemu-riscv64 並非單單模擬裸
3232
echo $? # 可以看到上一個程序的結束碼是 50
3333
```
3434

35-
`數據段.S` 首先在數據段 `.section .data` ,中用 `.word` 開闢 32 位元的空間,前方的``是一個標籤,在後續程式段的真言中會被代換為該空間的位址。
35+
`數據段.S` 首先在數據段 `.section .data` `.word` 開闢 32 位元的空間,前方的``是一個標籤,在後續程式段的真言中會被代換為該空間的位址。
3636

3737
再來看 `_start` 的第一行 `lw t0, 甲`,lw 是 load word 的縮寫,效果是從位址甲開始讀取 32 位元放進 t0 暫存器。 `lw` 執行完後, `t0` 就是 `50` 了,最後 `mv a0, t0` 把程序結束碼設為 50。
3838

@@ -65,3 +65,108 @@ sw rd, 標籤[31:12](rd) # 把被汙染的值寫到記憶體
6565
所以 `sw rd, 標籤, rt` 的第三個參數 rt 省不了。
6666

6767
## 運算
68+
69+
在語法樹中,`算式`是一棵二元樹,要從樹形轉換為循序、線性的真言並不是一目瞭然的事情。若先將算術二元樹轉換為後序表達式,計算的順序會比較明瞭一些。道友們在煉氣(學習編程基礎、資料結構)時,想必對後序表達式不陌生,後序拜訪二元樹就能得到。
70+
71+
```虛擬碼
72+
後序拜訪(樹) {
73+
後序拜訪(樹.左子樹)
74+
後序拜訪(樹.右子樹)
75+
打印(樹.值)
76+
}
77+
```
78+
79+
舉個例子:
80+
81+
```
82+
+
83+
/ \
84+
* 5
85+
/ \
86+
3 -
87+
/ \
88+
4 2
89+
```
90+
後序拜訪後,會打印出:
91+
92+
```
93+
3 4 2 - * 5 +
94+
```
95+
96+
這時計算的順序就很清楚了,由左而右是 - * + ,得到順序後,把每個數字放進一個暫存器,可以生成真言
97+
98+
```
99+
li t0, 3
100+
li t1, 4
101+
li t2, 2
102+
sub t1, t1, t2
103+
mul t0, t1, t1
104+
li t1, 5
105+
add t0, t1, t1
106+
```
107+
108+
然而算術樹可能會很高很畸形,導致暫存器的數量不夠用,這時就得把數字放進記憶體暫時儲存了。
109+
110+
道友們煉氣時應該都有印象,藉助資料結構「棧(堆疊)」可以很輕鬆的計算後序表達式,若需要複習,可以看看下方的逐步操作回憶一下:
111+
112+
```
113+
[3] # 遇數字 3,壓入棧
114+
[3, 4] # 遇數字 4,壓入棧
115+
[3, 4, 2] # 遇數字 2,壓入棧
116+
[3, 2] # 遇運算 -,計算 4 - 2 = 2,將結果壓入棧
117+
[6] # 遇運算 *,計算 3 * 2 = 6,將結果壓入棧
118+
[6, 5] # 遇數字 5,壓入棧
119+
[11] # 遇運算 +,計算 6 + 5 = 11,將結果壓入棧
120+
```
121+
122+
把所有運算都存到棧裡是很慢的,畢竟一次記憶體存取可能比是存取暫存器要慢上幾十乃至上百倍,即使快取命中也要好幾個 cycle 才拿得回來,但堆疊機實現起來容易。就先暫且把暫存器分配問題丟到一邊,來看看在精五真言中如何模擬堆疊機。
123+
124+
## 精五真言棧操作
125+
126+
精五(RISC-V)架構的棧一般來說是從高位址往低位址成長的。棧頂位址由暫存器 sp 記錄,`addi sp, sp, -8` 可擴大棧,相反地,`addi sp, sp, 8`會縮小棧。
127+
128+
以下精五真言將數字 3 壓入棧
129+
```assembly
130+
addi sp, sp, -8 # 擴大棧 64 位元(8 位元組 = 64 位元)
131+
li t0, 3 # t0 = 3
132+
sd t0, 0(sp) # sd 將 64 位元 t0 存到棧頂
133+
```
134+
135+
最後來看上述算術樹轉成堆疊機操作的完整精五真言:
136+
137+
```assembly
138+
# 3 入棧
139+
addi sp, sp, -8
140+
li t0, 3
141+
sd t0, 0(sp)
142+
# 4 入棧
143+
addi sp, sp, -8
144+
li t0, 4
145+
sd t0, 0(sp)
146+
# 2 入棧
147+
addi sp, sp, -8
148+
li t0, 2
149+
sd t0, 0(sp)
150+
# 減
151+
ld t1, 0(sp) # t1 = 棧頂
152+
addi sp, sp, 8 # 棧頂縮小 64 位元
153+
ld t0, 0(sp) # t0 = 棧頂
154+
sub t0, t0, t1 # t0 = t0 - t1
155+
sd t0, 0(sp) # sd 將 64 位元 t0 存到棧頂
156+
# 乘
157+
ld t1, 0(sp)
158+
addi sp, sp, 8
159+
ld t0, 0(sp)
160+
mul t0, t0, t1
161+
sd t0, 0(sp)
162+
# 5 入棧
163+
addi sp, sp, -8
164+
li t0, 5
165+
sd t0, 0(sp)
166+
# 加
167+
ld t1, 0(sp)
168+
addi sp, sp, 8
169+
ld t0, 0(sp)
170+
add t0, t0, t1
171+
sd t0, 0(sp)
172+
```

0 commit comments

Comments
 (0)