iT邦幫忙

2024 iThome 鐵人賽

DAY 18
0
Software Development

離塵指引.卷之一.試結丹:程式語言自舉系列 第 18

零.一版精五真言生成(一)變數儲存

  • 分享至 

  • xImage
  •  

至此,貧道已經展示了零.一版音界咒的分詞、剖析、語義檢查,若程式能通過這些步驟,代表它完全合法。接下來,就可以生成目標碼了,在本指引中,貧道會示範如何生成精五真言(RISC-V)。

變數儲存

熟悉 C 語言的道友想必都曉得,全域變數與區域變數在程序執行時是放在不同位置。全域變數會放在數據段(data section),在整個程序執行的過程都佔用一塊記憶體空間;而區域變數則是則是存放在函式調用時臨時開的棧禎。

零.一版音界咒的變數可以視做全域的,畢竟該版本尚無函式調用的概念。

精五真言的數據段

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

.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, 甲, rtrt 可以是任意通用暫存器。

選讀:為什麼 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 省不了。


上一篇
零.一版語義分析之符號檢查
下一篇
零.一版精五真言生成(二)堆疊機
系列文
離塵指引.卷之一.試結丹:程式語言自舉31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言