minrubyコンパイラ / 組み込み関数を呼び出す
libminruby.c で定義した組み込み関数を呼び出してみる。
関数の引数はパラメータレジスタ( x0 から x7)を経由して渡すのだが、パラメータレジスタは「セットした値が他の関数呼び出しに上書きされてしまう可能性のある」揮発性のレジスタである。思わぬ上書きを避けるため、評価した引数の値は一度スタックへ退避しておき、後でまとめてパラメータレジスタへ渡すと良い。
code:diff
diff --git a/libminruby.c b/libminruby.c
index 2b20040..a581930 100644
--- a/libminruby.c
+++ b/libminruby.c
@@ -5,3 +5,8 @@ long p(long n) {
printf("%ld\n", n);
return n;
}
+
+// 組み込み関数テスト用
+long my_add(long a, long b) {
+ return a + b;
+}
diff --git a/minrubyc.rb b/minrubyc.rb
index 3840382..94f1769 100644
--- a/minrubyc.rb
+++ b/minrubyc.rb
@@ -1,6 +1,9 @@
# minrubyc.rb
require "minruby"
+# 引数用レジスタの一覧
+PARAM_REGISTERS = %w(x0 x1 x2 x3 x4 x5 x6 x7)
+
# tree 内に含まれる、var_assign で定義される変数名の一覧
def var_names(tree)
@@ -94,11 +97,27 @@ def gen(tree, env)
# スタックを破棄
- elsif tree0 == "func_call" && tree1 == "p" - # p 関数を呼び出す
- gen(expr, env)
- puts "\tbl _p"
+ elsif tree0 == "func_call" +
+ # 引数用のレジスタは8つしかないので、引数が8個以上の場合はエラー
+ raise "too many arguments (given #{args.size}, expected 8)" if args.size > 8 +
+ # 引数を評価してスタックへ積む
+ args.reverse.each do |arg|
+ gen(arg, env)
+ puts "\tsub sp, sp, #16" + end
+
+ # スタックへ詰んだ引数の値を、引数用レジスタへセット
+ args.each_with_index do |arg, i|
+ puts "\tadd sp, sp, #16" + end
+
+ # 関数呼び出し
+ puts "\tbl _#{name}"
gen(stmt, env)
いい感じに動いてそう。
https://gyazo.com/a4e07dbb754b75cf75126caf387a9898