@@ -170,3 +170,146 @@ sd t0, 0(sp) # sd 將 64 位元 t0 存到棧頂
170170 add t0, t0, t1
171171 sd t0, 0(sp)
172172```
173+
174+ ## 實作
175+ 以下展示一份精五真言生成的實作。
176+
177+ 這是生成器的定義,其接收一個語法樹,把精五真言輸入到真言檔。變數集可以在符號檢查時取得。
178+ ``` rust
179+ pub struct O真言生成器 {
180+ 真言檔: File ,
181+ 語法樹: O語法樹,
182+ 變數集: HashSet <String >,
183+ }
184+
185+ impl O真言生成器 {
186+
187+ pub fn 生成(& mut self ) -> io :: Result <()> {
188+ self . 生成數據段()? ;
189+ self . 生成代碼段()
190+ }
191+
192+ ...
193+ }
194+ ```
195+ 真言生成分為「數據段」跟「代碼段」分開實作。
196+
197+ ### 數據段(變數儲存)
198+
199+ 數據段的生成頗為簡單,為每個變數在 ` .section .data ` 製造一相應的標籤,以 ` .quad ` 指定 64 位元空間。
200+
201+ ``` rust
202+ fn 生成數據段(& mut self ) -> io :: Result <()> {
203+ writeln! (self . 真言檔, " .section .data" )? ;
204+ self . 生成變數標籤()
205+ }
206+ fn 生成變數標籤(& mut self ) -> io :: Result <()> {
207+ for 變數 in & self . 變數集 {
208+ writeln! (self . 真言檔, " {}:" , 變數)? ;
209+ // 初始值為 0
210+ // 初始值是多少不重要,通過符號檢查,代表每個變數使用前都會先賦值
211+ writeln! (self . 真言檔, " \ t .quad 0" )? ;
212+ }
213+ self . 換行()
214+ }
215+ ```
216+
217+ ### (代碼段)運算
218+
219+ 代碼段就相對複雜許多,原理已在前文解釋,道友們可閱讀註解來幫助理解。
220+
221+ 這裡並沒有真的先轉換到後序表示法再生成代碼,而是對算術樹做後序遍歷的同時就把代碼給生成了。
222+
223+ ``` rust
224+ fn 生成代碼段(& mut self ) -> io :: Result <()> {
225+ writeln! (self . 真言檔, " .section .text" )? ;
226+ // 編譯器會將某些 .data 段的變數存放到 .sdata 段
227+ // .sdata 段的數據可以直接用 gp 暫存器的相對位址得到
228+ // 會快一個指令,但 gp 初始化需要導引
229+ // 因此此處採用 main 而非 _start
230+ // gcc 編譯時不加 -nostdlib 參數,讓 gcc 生成 _start 協助引導
231+ writeln! (self . 真言檔, " .global main" )? ;
232+ self . 換行()? ;
233+ writeln! (self . 真言檔, " main:" )? ;
234+ let 語法樹 = & self . 語法樹;
235+
236+ for 句 in & 語法樹. 句 {
237+ match 句 {
238+ O句:: 變數宣告(變數宣告) => Self :: 賦值(& mut self . 真言檔, & 變數宣告)? ,
239+ O句:: 算式(算式) => Self :: 計算(& mut self . 真言檔, & 算式)? ,
240+ }
241+ }
242+ writeln! (self . 真言檔, " # 結束" )? ;
243+ writeln! (self . 真言檔, " \ t li a7, 93" )? ; // RISCV Linux 中 exit 系統呼叫編號是 93
244+ writeln! (self . 真言檔, " \ t mv a0, t0" )? ; // a0 = t0
245+ writeln! (self . 真言檔, " \ t ecall" )? ; // 執行系統呼叫 exit(t0)
246+ Ok (())
247+ }
248+
249+ fn 賦值(真言檔: & mut File , 變數宣告: & O變數宣告) -> io :: Result <()> {
250+ Self :: 計算(真言檔, & 變數宣告. 算式)? ;
251+ writeln! (真言檔, " # 賦值給 {}" , & 變數宣告. 變數名)? ;
252+ writeln! (真言檔, " \ t sd t0, {}, s1" , & 變數宣告. 變數名) // 存入變數所在記憶體
253+ }
254+
255+ // 計算結束後,結果置於 t0
256+ fn 計算(真言檔: & mut File , 算式: & O算式) -> io :: Result <()> {
257+ match 算式 {
258+ O算式:: 二元運算(二元運算) => {
259+ Self :: 計算(真言檔, 二元運算. 左. as_ref ())? ;
260+ Self :: 計算(真言檔, 二元運算. 右. as_ref ())? ;
261+ Self :: 二元運算(真言檔, & 二元運算. 運算子)
262+ }
263+ O算式:: 數字(數) => Self :: 數字入棧(真言檔, 數),
264+ O算式:: 變數(變數) => Self :: 變數入棧(真言檔, 變數),
265+ }
266+ }
267+ // 結束時,t0 = 數
268+ fn 數字入棧(真言檔: & mut File , 數: & i64 ) -> io :: Result <()> {
269+ writeln! (真言檔, " # {} 入棧" , 數)? ;
270+
271+ writeln! (真言檔, " \ t addi sp, sp, -8" )? ; // 增加棧 64 位元的空間
272+ writeln! (真言檔, " \ t li t0, {}" , 數)? ; // 將 t0 設為「數」
273+ writeln! (真言檔, " \ t sd t0, 0(sp)" ) // t0 放入棧頂
274+ }
275+ // 結束時,t0 = 變數
276+ fn 變數入棧(真言檔: & mut File , 變數: & String ) -> io :: Result <()> {
277+ writeln! (真言檔, " # 變數「{}」入棧" , 變數)? ;
278+
279+ writeln! (真言檔, " \ t addi sp, sp, -8" )? ; // 增加棧 64 位元的空間
280+ writeln! (真言檔, " \ t ld t0, {}" , 變數)? ; // t0 = *(i64*)變數
281+ writeln! (真言檔, " \ t sd t0, 0(sp)" ) // t0 放入棧頂
282+ }
283+ // 結束時,t0 = 二元運算結果
284+ fn 二元運算(真言檔: & mut File , 運算子: & O運算子) -> io :: Result <()> {
285+ writeln! (真言檔, " # {:?}" , 運算子)? ;
286+
287+ writeln! (真言檔, " \ t ld t1, 0(sp)" )? ; // t1 = 棧頂
288+ writeln! (真言檔, " \ t addi sp, sp, 8" )? ; // 縮小棧
289+ writeln! (真言檔, " \ t ld t0, 0(sp)" )? ; // t0 = 棧頂
290+
291+ match 運算子 {
292+ O運算子:: 加 => {
293+ writeln! (真言檔, " \ t add t0, t0, t1" )? ;
294+ }
295+ O運算子:: 減 => {
296+ writeln! (真言檔, " \ t sub t0, t0, t1" )? ;
297+ }
298+ O運算子:: 乘 => {
299+ writeln! (真言檔, " \ t mul t0, t0, t1" )? ;
300+ }
301+ O運算子:: 除 => {
302+ writeln! (真言檔, " \ t div t0, t0, t1" )? ;
303+ }
304+ }
305+
306+ writeln! (真言檔, " \ t sd t0, 0(sp)" ) // t0 放入棧頂
307+ }
308+ ```
309+
310+ ### 組譯執行
311+
312+ ```
313+ riscv64-unknown-elf-gcc {{target}}.S # 不加 -nostdlib 參數
314+ qemu-riscv64 a.out
315+ ```
0 commit comments