Skip to content

Commit

Permalink
Merge pull request #2 from toririm/feature/def-function
Browse files Browse the repository at this point in the history
[WIP] ステップ15: 関数の定義に対応する
  • Loading branch information
toririm authored Jan 12, 2025
2 parents 9a334fd + 4a9b1fe commit 8cb77f6
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 147 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: test

on:
- push

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run test
run: make test
24 changes: 19 additions & 5 deletions 0cc.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <stdlib.h>
#include <string.h>

// 関数の引数レジスタ
extern char *ARG_RGST[];

// トークンの種類
typedef enum {
TK_RESERVED, // 記号
Expand Down Expand Up @@ -45,6 +48,7 @@ typedef enum {
ND_FOR_UPDT_STMT,
ND_BLOCK,
ND_FUNC_CALL,
ND_FUNC,
} NodeKind;

typedef struct Node Node;
Expand All @@ -54,9 +58,10 @@ struct Node {
Node *lhs;
Node *rhs;
int val; // ND_NUMの値, if/for/whileなどのlabel_index
int offset; // ND_LVAR
Node *nodes[100]; // ND_BLOCKの中身
char *func_name; // ND_FUNC_CALL
int offset; // ND_LVARの変数, ND_FUNCの総変数のベースポインタからのオフセット
Node *stmts[100]; // ND_BLOCK, ND_FUNCの中身
Node *args[7]; // ND_FUNC, ND_FUNC_CALLの引数, 6 + NULL
char *name; // ND_FUNC, ND_FUNC_CALLの関数名
};

typedef struct LVar LVar;
Expand All @@ -74,10 +79,12 @@ extern char *user_input;
// 現在着目しているトークン
extern Token *token;

// stmtのASTの配列
// funcのASTの配列
extern Node *code[100];

// ローカル変数
// パーサは関数ごとのパースに対して独立なので
// 一時変数としてグローバルに定義しても問題ない
extern LVar *locals;

// ラベルの unique identifier
Expand All @@ -93,12 +100,18 @@ void expect(char *op);
int expect_number();
Token *consume_ident();
bool at_eof();
bool is_alnum(char c);
LVar *find_lvar(Token *tok);
LVar *new_lvar(char *name, int len);
char *strcopy_n(char *src, int n);
Token *new_token(TokenKind kind, Token *cur, char *str);
Token *tokenize(char *p);

Node *new_node(NodeKind kind, Node *lhs, Node *rhs);
Node *new_node_num(int val);
Node *new_node_lvar(Token *tok);
void program();
Node *func();
Node *stmt();
Node *expr();
Node *assign();
Expand All @@ -115,5 +128,6 @@ void gen_lval(Node *node);
void gen_if(Node *node);
void gen_for_cond(Node *node);
void gen_for_updt_stmt(Node *node);
void gen_func(Node *node);
void gen_func_call(Node *node);
void gen(Node *node);
void gen_func(Node *node);
93 changes: 52 additions & 41 deletions codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,59 +49,35 @@ void gen_for_updt_stmt(Node *node) {
printf(" jmp .Lbegin%d\n", node->val);
}

void gen_func(Node *node) {
char *args[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" };
void gen_func_call(Node *node) {

int i = 0;
for (; node->nodes[i] && i < 6; i++) {
gen(node->nodes[i]);
for (; node->args[i] && i < 6; i++) {
gen(node->args[i]);
}
i--;
while (i >= 0) printf(" pop %s\n", args[i--]);
while (i >= 0) printf(" pop %s\n", ARG_RGST[i--]);

/*
x86-64のABIのため
RSPが16の倍数になるように調整
*/

// 引数の数, 第3引数, 第1引数として使われるRAX, RDX, RDIを一旦退避
printf(" push rax\n");
printf(" push rdx\n");
printf(" push rdi\n");

// RSP % 16
printf(" mov rdi, 16\n");
// 現在のRSPを保存
printf(" mov rax, rsp\n");
printf(" cqo\n");
printf(" idiv rdi\n");
// 商->RAX, 余り->RDX

// if (RSP % 16)
printf(" cmp rdx, 0\n");
printf(" je .Lelse%d\n", node->val);
// スタックに退避していたRAX, RDX, RDIを戻す
printf(" pop rdi\n");
printf(" pop rdx\n");
printf(" pop rax\n");
// popやpushは8byte単位で動かすのでズレている場合は決めうちで8下げる
// RSP -= 8;
// 16バイトアラインメントをチェック
printf(" and rax, 15\n");
printf(" jz .Lcall%d\n", node->val);
// アラインメントが必要な場合
printf(" sub rsp, 8\n");
printf(" call %s\n", node->func_name);
printf(" call %s\n", node->name);
printf(" add rsp, 8\n");
printf(" push rax\n");
printf(" jmp .Lend%d\n", node->val);

// else
printf(".Lelse%d:\n", node->val);
// スタックに退避していたRAX, RDX, RDIを戻す
printf(" pop rdi\n");
printf(" pop rdx\n");
printf(" pop rax\n");
printf(" call %s\n", node->func_name);
printf(" push rax\n");

// end if
// アラインメントが不要な場合
printf(".Lcall%d:\n", node->val);
printf(" call %s\n", node->name);
printf(".Lend%d:\n", node->val);
printf(" push rax\n");
}

void gen(Node *node) {
Expand Down Expand Up @@ -162,14 +138,14 @@ void gen(Node *node) {
case ND_BLOCK:
int i = 0;
// stmts を取り出す
while (node->nodes[i]) {
gen(node->nodes[i++]);
while (node->stmts[i]) {
gen(node->stmts[i++]);

printf(" pop rax\n");
}
return;
case ND_FUNC_CALL:
gen_func(node);
gen_func_call(node);
return;
}

Expand Down Expand Up @@ -224,3 +200,38 @@ void gen(Node *node) {

printf(" push rax\n");
}

void gen_func(Node *node) {
if (node->kind != ND_FUNC)
error("ND_FUNC expected but found %d\n", node->kind);

printf("%s:\n", node->name);

// プロローグ
printf(" push rbp\n");
printf(" mov rbp, rsp\n");
printf(" sub rsp, %d\n", node->offset);

// 関数の引数のレジスタの値を変数に保存
int arg_idx = 0;
Node *arg;
while (arg = node->args[arg_idx]) {
gen_lval(arg);
printf(" pop rax\n");
printf(" mov [rax], %s\n", ARG_RGST[arg_idx++]);
}

Node *stmt;
for (int i = 0; stmt = node->stmts[i]; i++) {
gen(stmt);

printf(" pop rax\n");
}

// エピローグ
// 変数で確保しておいたRSPをRBPと同じ場所に戻す
printf(" mov rsp, rbp\n");
// popでRSPがリターンアドレスを指し、RBPを関数呼び出し前のRBPに戻す
printf(" pop rbp\n");
printf(" ret\n");
}
26 changes: 9 additions & 17 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "0cc.h"

char *ARG_RGST[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" };

char *user_input;
Token *token;
Node *code[100];
Expand All @@ -19,27 +21,17 @@ int main(int argc, char **argv) {

// アセンブリの前半部分を出力
printf(".intel_syntax noprefix\n");
printf(".globl main\n");
printf("main:\n");

// プロローグ
// 26 * 8 = 208, 変数26個分の領域を確保する
printf(" push rbp\n");
printf(" mov rbp, rsp\n");
printf(" sub rsp, 208\n");

printf(".globl ");
for (int i = 0; code[i]; i++) {
gen(code[i]);
if (i > 0) printf(", ");
printf("%s", code[i]->name);
}
printf("\n");

// 式の評価結果のスタックを取り除く
printf(" pop rax\n");
for (int i = 0; code[i]; i++) {
gen_func(code[i]);
}

// エピローグ
// 変数で確保しておいたRSPをRBPと同じ場所に戻す
printf(" mov rsp, rbp\n");
// popでRSPがリターンアドレスを指し、RBPを関数呼び出し前のRBPに戻す
printf(" pop rbp\n");
printf(" ret\n");
return 0;
}
Loading

0 comments on commit 8cb77f6

Please sign in to comment.