TinyRubyコンパイラ/整数リテラルの評価
整数リテラルをリターンコードとして返すだけのコンパイラを作成する。
C言語で書くとこんな感じ。
code:return123.c
int main() {
return 123;
}
Cからアセンブリを出力すると、
code:sh
gcc -S -masm=intel -fno-asynchronous-unwind-tables return123.c
以下のアセンブリコードが出力される。
code:return123.s
.file "return123.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
push rbp
mov rbp, rsp
mov eax, 123
pop rbp
ret
.size main, .-main
.ident "GCC: (GNU) 13.2.0"
.section .note.GNU-stack,"",@progbits
不要なアセンブリコードを取り除くとこんな感じ。
code:asm
.intel_syntax noprefix
.globl main
main:
; 関数プロローグ
; ベースポインタ(rbp) の値をスタックへ退避し、
; スタックポインタ(rsp)の値を新しいベースポインタ(rbp)の値として設定する
push rbp
mov rbp, rsp
; メイン処理
; eaxレジスタにリターンコード 123 をセット
mov eax, 123
; 関数エピローグ
; ベースポインタ(rbp)とスタックポインタ(rsp)の値を関数呼び出し前の状態に戻す
pop rbp
ret
このアセンブリコードを参考に、リターンコードを返すだけのコンパイラをRubyで作成する。
整数リテラル 10 をパースすると、以下のような構文木が返るので、
code:ruby
irb(main):001:0> minruby_parse "10"
以下のようなコンパイラを作成。
code:tinyrubyc.rb
require 'minruby'
# 入力をパースする
node = minruby_parse(gets)
puts " .intel_syntax noprefix"
puts " .globl main"
puts "main:"
puts " push rbp"
puts " mov rbp, rsp"
# メイン処理
# 整数の場合
end
puts " pop rbp"
puts " ret"
コンパイラが出力したアセンブリコードはこんな感じ。
code:sh
$ echo 123 | ruby tinyrubyc.rb > tmp.s
$ cat tmp.s
.intel_syntax noprefix
.globl main
main:
push rbp
mov rbp, rsp
mov eax, 123
pop rbp
ret
コンパイラが出力したアセンブリコードをビルドして実行する。リターンコード 123 が出力されればOK
code:sh
$ gcc -z noexecstack tmp.s
$ ./a.out
$ echo $?
123