TinyRubyコンパイラ/四則演算の評価
整数リテラルが評価できるようになったので、次は四則演算を計算する。
構文木を確認
四則演算の出力する構文木はこんな感じ。
code:irb
irb(main):001:0> minruby_parse "10 + 20"
irb(main):002:0> minruby_parse "10 - 20"
irb(main):003:0> minruby_parse "10 * 20 + 30"
irb(main):004:0> minruby_parse "10 + 2 * 10 - 10 / 2"
=> ["+", "lit", 10, ["*", "lit", 2, "lit", 10], "lit", 10, "lit", 2] 四則演算
四則演算を実装する。
code:diff
diff --git a/tinyrubyc.rb b/tinyrubyc.rb
index 1a7a6b9..b756a43 100644
--- a/tinyrubyc.rb
+++ b/tinyrubyc.rb
@@ -6,6 +6,42 @@ def gen(node)
# 整数の場合
+ elsif %w(+ - * /).include?(node0) + # 四則演算の場合
+
+ # r12 と r13 の値をスタックへ退避
+ puts " push r12"
+ puts " push r13"
+
+ # 左辺の計算結果を r12 へ格納
+ puts " mov r12, rax"
+
+ # 右辺の計算結果を r13 へ格納
+ puts " mov r13, rax"
+
+ # 演算結果を rax へ格納
+ when "+"
+ puts " add r12, r13"
+ puts " mov rax, r12"
+ when "-"
+ puts " sub r12, r13"
+ puts " mov rax, r12"
+ when "*"
+ puts " imul r12, r13"
+ puts " mov rax, r12"
+ when "/"
+ puts " mov rax, r12"
+ puts " cqo"
+ puts " idiv r13"
+ end
+
+ # r12 と r13 の値をスタックから復元
+ puts " pop r13"
+ puts " pop r12"
end
end
コンパイラが出力するアセンブリコードはこんな感じ。
code:sh
$ echo "10 + 20" | ruby tinyrubyc.rb > tmp.s
$ cat tmp.s
.intel_syntax noprefix
.globl main
main:
; 関数プレリュード
push rbp
mov rbp, rsp
; r12とr13の値を退避
push r12
push r13
; r12 = 10
mov rax, 10
mov r12, rax
; r13 = 20
mov rax, 20
mov r13, rax
; rax = r12 + r13
add r12, r13
mov rax, r12
; 退避していたr12とr13の値を復元
pop r13
pop r12
; 関数エピローグ
pop rbp
ret
コンパイラが出力したアセンブリコードをビルドして実行する。「10 + 20」の結果の「30」がリターンコードとして返ればOK
code:sh
$ gcc -z noexecstack tmp.s
$ ./a.out
$ echo $?
30
リターンコードの最大値
リターンコードの最大値は「255」なため、255を超える値は正しく表示されないので注意。
code:sh
$ echo "255 + 10" | ruby tinyrubyc.rb > tmp.s
$ gcc -z noexecstack tmp.s && ./a.out
$ echo $?
9