[C] コンパイラ作成 (関数呼び出しに対応する)

関数呼び出しに対応するの大変でした。
正しいかわかりませんが、生成規則を以下のように定義しました。
(試行錯誤のすえ以下のようになったのですが...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
program: stmt program
program: ε
 
stmt: assign ";"
 
assign: equality
assign: equality "=" assign
 
equality: add
equality: add "==" equality
equality: add "!=" equality
 
add: mul
add: add "+" mul
add: add "-" mul
 
mul: func
mul: mul "*" func
mul: mul "/" func
 
func: ident "(" argument ")"
func: term
 
argument: equality
argument: equality "," equality
argument: ε
 
term: num
term: ident
term: "(" assign ")"
 
digit: "0" | "1" | "2" | "3" | "4" | "5 | "6" | "7" | "8" | "9"
ident: "a" - "z"

funcの定義をmulより下に持ってきてさらに引数用のargumentを定義しました。
新しくfuncとargumentを導入したので、コード生成の部分にもそれぞれのパターンを定義してパースしたらうまく動きました。
一旦最大の引数は6個までを制限にしています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void gen(Node *node) {
:
    if (node->ty == ND_FUNCTION) {
        // 引数がある場合は引数を評価
        if (node->rhs != NULL) {
            gen(node->rhs);
        }
 
        // 引数をレジスタにセット
        gen_arguments(node->name, node->val);
 
        // 関数名を指定して実行
        printf("  call %s\n", node->name);
        // 結果をスタックに入れる
        printf("  push rax\n");
        return;
    }
 
    if (node->ty == ND_ARGUMENT) {
        // 引数を評価
        gen(node->lhs);
        gen(node->rhs);
        return;
    }
:

あと、RSPを16の倍数にするとの事ですが、変数領域を確保するところで16の倍数にするようにしたのですがよかったのでしょうか。

1
2
3
4
5
6
7
8
9
10
11
12
:
// プロローグ
// 変数の数分の領域を確保する
// 関数呼び出しをする際にはRSPが16の倍数になっている必要があるとのこと
// ココで調整しているけど、正しい?
int size_of_variables = variables->keys->len * SIZE_OF_ADDRESS;
int rsp_offset = size_of_variables % 16;
size_of_variables += rsp_offset;
printf("  push rbp\n");
printf("  mov rbp, rsp\n");
printf("  sub rsp, %d\n", size_of_variables);
:

実装したものが動作するかどうかはテスト用の関数が入ったソースを用意して 実際に動作させて確認します。

$ ./9cc "a = hoge(1, 2) * 3;" > tmp.s
$ gcc -o tmp tmp.s test.o
$ ./tmp
$ echo $?   # 結果の値になるはず

ひとまず動作したのでよしとします。

参考URL

0 件のコメント :

コメントを投稿