-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
72 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
至此,貧道已經展示了零.一版音界咒的分詞、剖析、語義檢查,若程式能通過這些步驟,代表它完全合法。接下來,就可以生成目標碼了,在本指引中,貧道會示範如何生成精五真言(RISC-V)。 | ||
|
||
## 變數儲存 | ||
熟悉 C 語言的道友想必都曉得,全域變數與區域變數在程序執行時是放在不同位置。全域變數會放在數據段(data section),在整個程序執行的過程都佔用一塊記憶體空間;而區域變數則是則是存放在函式調用時臨時開的棧禎。 | ||
|
||
零.一版音界咒的變數可以視做全域的,畢竟該版本尚無函式調用的概念。 | ||
|
||
### 精五真言的數據段 | ||
|
||
很容易就能用精五真言設定全域變數,一下是範例`數據段.S`: | ||
|
||
```assembly | ||
.section .data | ||
甲: | ||
.word 50 # .word 表示開闢 32 位元的空間,該空間的值為 50 | ||
.section .text | ||
.global _start | ||
_start: | ||
lw t0, 甲 # t0 = *(u32*)甲 | ||
li a7, 93 # RISCV Linux 中 exit 系統呼叫編號是 93 | ||
mv a0, t0 # a0 = t0 | ||
ecall # 執行系統呼叫 exit(t0) | ||
``` | ||
|
||
同樣交叉編譯並以 qemu 模擬 | ||
``` | ||
riscv64-unknown-elf-gcc -nostdlib 數據段.S # 編譯後應得 a.out 檔案 | ||
qemu-riscv64 a.out # qemu-riscv64 並非單單模擬裸機,還實作了部分系統呼叫 | ||
echo $? # 可以看到上一個程序的結束碼是 50 | ||
``` | ||
|
||
`數據段.S` 首先在數據段 `.section .data` ,中用 `.word` 開闢 32 位元的空間,前方的`甲`是一個標籤,在後續程式段的真言中會被代換為該空間的位址。 | ||
|
||
再來看 `_start` 的第一行 `lw t0, 甲`,lw 是 load word 的縮寫,效果是從位址甲開始讀取 32 位元放進 t0 暫存器。 `lw` 執行完後, `t0` 就是 `50` 了,最後 `mv a0, t0` 把程序結束碼設為 50。 | ||
|
||
與 `lw` 相對應,`sw`(store word)能夠將暫存器的值寫入某個記憶體位址,但 `sw` 要多加一個參數,`sw t0, 甲, rt`, `rt` 可以是任意通用暫存器。 | ||
|
||
|
||
### 選讀:為什麼 sw 要三個參數 | ||
接三個參數的 `sw rd, 標籤, rt` 是偽指令,組譯後會變為兩條指令: | ||
|
||
``` | ||
auipc rt, 標籤[31:12] | ||
sw rd, 標籤[31:12](rt) | ||
``` | ||
|
||
其大概意思是用先把 rt 的值弄成成標籤的位址,再把 rt 所在位址的 32 位元存進 rd 。 | ||
|
||
那為何 `lw rd, 標籤` 就不需要額外暫存器來幫忙存位址呢?它也是會編譯成兩條真言的偽指令吧! 因為`rd`在載入時能重複用。 | ||
``` | ||
auipc rd, 標籤[31:12] # 反正等等 rd 的值等等也要被改了,順便當位址用 | ||
lw rd, 標籤[31:12](rd) # rd 既是記憶體位址,又是要被寫入的暫存器 | ||
``` | ||
|
||
`sw` 的 rd 要是重複用,還沒把它的值寫到記憶體,自己就先被汙染了: | ||
|
||
``` | ||
auipc rd, 標籤[31:12] # rd 的值已被汙染 | ||
sw rd, 標籤[31:12](rd) # 把被汙染的值寫到記憶體 | ||
``` | ||
|
||
所以 `sw rd, 標籤, rt` 的第三個參數 rt 省不了。 | ||
|
||
## 運算 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters