chibiccを読む~Cコンパイラコードリーディング~ ステップ20, 21, 22
トップページ
jupiteroak.hatenablog.com
「低レイヤを知りたい人のためのCコンパイラ作成入門」のCコンパイラを読んでいきます。
www.sigbus.info
ステップ21に該当
github.com
ステップ22に該当
github.com
ステップ20に該当
github.com
- 今回作成するコンパイラ
- 追加・修正されたコンパイラのソースコード(ステップ21、配列を扱えるコンパイラを実装する)
- 追加・修正されたコンパイラのソースコード(ステップ22、配列の添字を扱えるコンパイラを作成する)
- 追加・修正されたコンパイラのソースコード(ステップ20、sizeof演算子を扱えるコンパイラを作成する)
- starts_with_reserved関数
- NodeKind
- primary関数
- visit関数
- ノードがセットされていない場合(変更なし)
- 全ての子のノードに対してvisit関数を再帰的に呼び出す(変更なし)
- ノード型に応じて型付け処理を行う(int型を付ける、変更なし)
- ノード型に応じて型付け処理を行う(ND_VAR、変更なし)
- ノード型に応じて型付け処理を行う(ND_ADD、変更なし)
- ノード型に応じて型付け処理を行う(ND_SUB、変更なし)
- ノード型に応じて型付け処理を行う(ND_ASSIGN、変更なし)
- ノード型に応じて型付け処理を行う(ND_ADDR、変更なし)
- ノード型に応じて型付け処理を行う(ND_DEREF、変更なし)
- ノード型に応じて型付け処理を行う(ND_SIZEOF)
- テストコード
- Makefile
今回作成するコンパイラ
配列を実装する(ステップ21、配列を扱えるコンパイラを作成する)
↓
配列の添字を実装する(ステップ22、配列の添字を扱えるコンパイラを作成する)
↓
sizeof演算子(ステップ20、sizeof演算子を扱えるコンパイラを作成する)
追加・修正されたコンパイラのソースコード(ステップ21、配列を扱えるコンパイラを実装する)
main関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-a0cb465674c1b01a07d361f25a0ef2b0214b7dfe9412b7777f89add956da10ecR17
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/main.c#L17
int main(int argc, char **argv) { if (argc != 2) error("%s: invalid number of arguments", argv[0]); // Tokenize and parse. user_input = argv[1]; token = tokenize(); Function *prog = program(); add_type(prog); // Assign offsets to local variables. for (Function *fn = prog; fn; fn = fn->next) { int offset = 0; for (VarList *vl = fn->locals; vl; vl = vl->next) { Var *var = vl->var; offset += size_of(var->ty); var->offset = offset; } fn->stack_size = offset; } // Traverse the AST to emit assembly. codegen(prog); return 0; }
コマンドライン引数の個数をチェックする(変更なし)
if (argc != 2) error("%s: invalid number of arguments", argv[0]);
トークナイズを行う(変更なし)
// Tokenize and parse. user_input = argv[1]; token = tokenize();
パースを行う(変更なし)
Function *prog = program();
型付けを行う(変更なし)
add_type(prog);
引数・ローカル変数のアドレスとスタックサイズを定める
// Assign offsets to local variables. for (Function *fn = prog; fn; fn = fn->next) { int offset = 0; for (VarList *vl = fn->locals; vl; vl = vl->next) { Var *var = vl->var; offset += size_of(var->ty); var->offset = offset; } fn->stack_size = offset; }
引数やローカル変数に割り当てるアドレス(スタックのベースアドレスからのオフセットアドレス)を8バイトからsize_of(var->ty)バイト(型のバイトサイズ)へ変更します。
アセンブリコードを生成する(変更なし)
// Traverse the AST to emit assembly. codegen(prog); return 0;
tokenize関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R173
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/tokenize.c#L173
Token *tokenize() { char *p = user_input; Token head; head.next = NULL; Token *cur = &head; while (*p) { // Skip whitespace characters. if (isspace(*p)) { p++; continue; } // Keyword or multi-letter punctuator char *kw = starts_with_reserved(p); if (kw) { int len = strlen(kw); cur = new_token(TK_RESERVED, cur, p, len); p += len; continue; } // Single-letter punctuator if (strchr("+-*/()<>;={},&[]", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; } // Identifier if (is_alpha(*p)) { char *q = p++; while (is_alnum(*p)) p++; cur = new_token(TK_IDENT, cur, q, p - q); continue; } // Integer literal if (isdigit(*p)) { cur = new_token(TK_NUM, cur, p, 0); char *q = p; cur->val = strtol(p, &p, 10); cur->len = p - q; continue; } error_at(p, "invalid token"); } new_token(TK_EOF, cur, p, 0); return head.next; }
文字列の先頭アドレスを取得する(変更なし)
char *p = user_input;
トークンからなる連結リストのヘッダーを作成する(変更なし)
Token head; head.next = NULL; Token *cur = &head;
空白文字の場合(変更なし)
while (*p) { // Skip whitespace characters. if (isspace(*p)) { p++; continue; }
キーワードの場合(変更なし)
// Keyword or multi-letter punctuator char *kw = starts_with_reserved(p); if (kw) { int len = strlen(kw); cur = new_token(TK_RESERVED, cur, p, len); p += len; continue; }
1文字の記号の場合
// Single-letter punctuator if (strchr("+-*/()<>;={},&[]", *p)) { cur = new_token(TK_RESERVED, cur, p++, 1); continue; }
第一引数の文字列に"["と"]"を追加し、"["と"]"をトークンとして扱えるようにします。
識別子の場合(変更なし)
// Identifier if (is_alpha(*p)) { char *q = p++; while (is_alnum(*p)) p++; cur = new_token(TK_IDENT, cur, q, p - q); continue; }
数字の場合(変更なし)
// Integer literal if (isdigit(*p)) { cur = new_token(TK_NUM, cur, p, 0); char *q = p; cur->val = strtol(p, &p, 10); cur->len = p - q; continue; }
その他の場合(変更なし)
error_at(p, "invalid token"); }
連結リストの先頭トークンを戻り値としてリターンする(変更なし)
return head.next;
declaration関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR155
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/parse.c#L155
// declaration = basetype ident ("[" num "]")* ("=" expr) ";" Node *declaration() { Token *tok = token; Type *ty = basetype(); char *name = expect_ident(); ty = read_type_suffix(ty); Var *var = push_var(name, ty); if (consume(";")) return new_node(ND_NULL, tok); expect("="); Node *lhs = new_var(var, tok); Node *rhs = expr(); expect(";"); Node *node = new_binary(ND_ASSIGN, lhs, rhs, tok); return new_unary(ND_EXPR_STMT, node, tok); }
declaration関数は、生成規則 declaration = basetype ident ("[" num "]")* ("=" expr)? ";" に基づいて、抽象構文木のノードを生成します。
basetype(変更なし)
Type *ty = basetype();
ident
char *name = expect_ident();
この時点では、トークンから読み取った識別子が変数名か配列名かわからないので、expect_ident関数を呼び出し名前を保存しておきます。
「"["とnumと"]"」を0回以上
ty = read_type_suffix(ty); Var *var = push_var(name, ty);
read_type_suffix関数を呼び出して、トークンを読み取りながら、データ型が配列型がそれ以外の型かを決定します。
その後、配列名かローカル変数名を表現するVar構造体を生成します。
「"="とexpr」を0回か1回(変更なし)
if (consume(";")) return new_node(ND_NULL, tok); expect("="); Node *lhs = new_var(var, tok); Node *rhs = expr();
";"(変更なし)
expect(";"); Node *node = new_binary(ND_ASSIGN, lhs, rhs, tok); return new_unary(ND_EXPR_STMT, node, tok);
read_func_param関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR105
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/parse.c#L105
VarList *read_func_param() { Type *ty = basetype(); char *name = expect_ident(); ty = read_type_suffix(ty); VarList *vl = calloc(1, sizeof(VarList)); vl->var = push_var(name, ty); return vl; }
Type構造体を生成する(変更なし)
Type *ty = basetype();
変数名か配列名になる識別子を取得する
char *name = expect_ident();
この時点では、トークンから読み取った識別子が変数名か配列名かわからないので、expect_ident関数を呼び出し名前を保存しておきます。
Type構造体を更新する
ty = read_type_suffix(ty);
read_type_suffix関数を呼び出して、トークンを読み取りながら、データ型が配列型がそれ以外の型かを決定します。
VarList構造体を生成する(変更なし)
VarList *vl = calloc(1, sizeof(VarList));
Var構造体を生成する(変更なし)
vl->var = push_var(name, ty);
read_type_suffix関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR94
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/parse.c#L94
Type *read_type_suffix(Type *base) { if (!consume("[")) return base; int sz = expect_number(); expect("]"); base = read_type_suffix(base); return array_of(base, sz); }
read_type_suffix関数は、トークンを読み取って、データ型が配列型か配列型以外かを決定します。
!consume("[") が真となる場合 → 現在着目しているトークンが"["ではなかった場合は、Type構造体baseを戻り値としてリターンします。
"["の次のトークンは、配列のサイズを指定する整数値であることが期待されているので、expect_number関数を呼び出して、その整数値を取得します。
多次元配列に対応するために、read_type_suffix関数を再帰的に呼び出し、baseを更新します。
最後に、array_of関数を呼び出して、配列型を表現するType構造体を生成します。
TypeKind
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R139
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/chibicc.h#L139
typedef enum { TY_INT, TY_PTR, TY_ARRAY } TypeKind;
配列型を表現するTY_ARRAYを追加します。
Type構造体
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R144
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/chibicc.h#L144
struct Type { TypeKind kind; Type *base; int array_size; };
配列のサイズを記録するためにarray_sizeメンバを追加します。
array_of関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R16
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L16
Type *array_of(Type *base, int size) { Type *ty = calloc(1, sizeof(Type)); ty->kind = TY_ARRAY; ty->base = base; ty->array_size = size; return ty; }
array_of関数は、配列型を表現するType構造体を生成します。
size_of関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R24
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L24
int size_of(Type *ty) { if (ty->kind == TY_INT || ty->kind == TY_PTR) return 8; assert(ty->kind == TY_ARRAY); return size_of(ty->base) * ty->array_size; }
sizet_of関数は、引数で指定されType構造体が表現する型に応じて、その型のサイズを取得します。
visit関数
void visit(Node *node) { if (!node) return; visit(node->lhs); visit(node->rhs); visit(node->cond); visit(node->then); visit(node->els); visit(node->init); visit(node->inc); for (Node *n = node->body; n; n = n->next) visit(n); for (Node *n = node->args; n; n = n->next) visit(n); switch (node->kind) { case ND_MUL: case ND_DIV: case ND_EQ: case ND_NE: case ND_LT: case ND_LE: case ND_FUNCALL: case ND_NUM: node->ty = int_type(); return; case ND_VAR: node->ty = node->var->ty; return; case ND_ADD: if (node->rhs->ty->base) { Node *tmp = node->lhs; node->lhs = node->rhs; node->rhs = tmp; } if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return; case ND_SUB: if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return; case ND_ASSIGN: node->ty = node->lhs->ty; return; case ND_ADDR: if (node->lhs->ty->kind == TY_ARRAY) node->ty = pointer_to(node->lhs->ty->base); else node->ty = pointer_to(node->lhs->ty); return; case ND_DEREF: if (!node->lhs->ty->base) error_tok(node->tok, "invalid pointer dereference"); node->ty = node->lhs->ty->base; return; } }
ノードがセットされていない場合(変更なし)
if (!node) return;
全ての子のノードに対してvisit関数を再帰的に呼び出す(変更なし)
visit(node->lhs); visit(node->rhs); visit(node->cond); visit(node->then); visit(node->els); visit(node->init); visit(node->inc); for (Node *n = node->body; n; n = n->next) visit(n); for (Node *n = node->args; n; n = n->next) visit(n);
ノード型に応じて型付け処理を行う(int型を付ける、変更なし)
switch (node->kind) { case ND_MUL: case ND_DIV: case ND_EQ: case ND_NE: case ND_LT: case ND_LE: case ND_FUNCALL: case ND_NUM: node->ty = int_type(); return;
ノード型に応じて型付け処理を行う(ND_VAR、変更なし)
case ND_VAR: node->ty = node->var->ty; return;
ノード型に応じて型付け処理を行う(ND_ADD)
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R63
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L63
case ND_ADD: if (node->rhs->ty->base) { Node *tmp = node->lhs; node->lhs = node->rhs; node->rhs = tmp; } if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return;
子ノードrhsの型が「~へのポインタ」型であるかどうかを、子ノードが持つType構造体がさらにType構造体を参照しているかどうかで、判断するようにします。
ノード型に応じて型付け処理を行う(ND_SUB)
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R73
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L73
case ND_SUB: if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return;
子ノードrhsの型が「~へのポインタ」型であるかどうかを、子ノードが持つType構造体がさらにType構造体を参照しているかどうかで、判断するようにします。
ノード型に応じて型付け処理を行う(ND_ASSIGN、変更なし)
case ND_ASSIGN: node->ty = node->lhs->ty; return;
ノード型に応じて型付け処理を行う(ND_ADDR)
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R81
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L81
case ND_ADDR: if (node->lhs->ty->kind == TY_ARRAY) node->ty = pointer_to(node->lhs->ty->base); else node->ty = pointer_to(node->lhs->ty); return; }
子ノードlhsの持つType構造体が配列型を表現している場合は(子ノードlhsの持つ型が配列型の場合は)、その配列型を表現しているType構造体が参照しているType構造体を用いてpointer_to関数を呼び出し、親ノードが持つ型(Type構造体)を「base型へのポインタ」型にします。
ノード型に応じて型付け処理を行う(ND_DEREF)
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R87
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/type.c#L87
case ND_DEREF: if (!node->lhs->ty->base) error_tok(node->tok, "invalid pointer dereference"); node->ty = node->lhs->ty->base; return;
子ノードlhsの型が「~へのポインタ」型であるかどうかを、子ノードが持つType構造体がさらにType構造体を参照しているかどうかで、判断するようにします。
また、!node->lhs->ty->baseが真となる場合 → 子ノードが持つType構造体がさらにType構造体を参照していなかった場合 → 子ノードlhsの型が「~へのポインタ」型でなかった場合は、 error_tok関数を呼び出してエラーメッセージを出力します。
gen関数
void gen(Node *node) { switch (node->kind) { case ND_NULL: return; case ND_NUM: printf(" push %d\n", node->val); return; case ND_EXPR_STMT: gen(node->lhs); printf(" add rsp, 8\n"); return; case ND_VAR: gen_addr(node); if (node->ty->kind != TY_ARRAY) load(); return; case ND_ASSIGN: gen_lval(node->lhs); gen(node->rhs); store(); return; case ND_ADDR: gen_addr(node->lhs); return; case ND_DEREF: gen(node->lhs); if (node->ty->kind != TY_ARRAY) load(); return; case ND_IF: { int seq = labelseq++; if (node->els) { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lelse%d\n", seq); gen(node->then); printf(" jmp .Lend%d\n", seq); printf(".Lelse%d:\n", seq); gen(node->els); printf(".Lend%d:\n", seq); } else { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); gen(node->then); printf(".Lend%d:\n", seq); } return; } case ND_WHILE: { int seq = labelseq++; printf(".Lbegin%d:\n", seq); gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); gen(node->then); printf(" jmp .Lbegin%d\n", seq); printf(".Lend%d:\n", seq); return; } case ND_FOR: { int seq = labelseq++; if (node->init) gen(node->init); printf(".Lbegin%d:\n", seq); if (node->cond) { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); } gen(node->then); if (node->inc) gen(node->inc); printf(" jmp .Lbegin%d\n", seq); printf(".Lend%d:\n", seq); return; } case ND_BLOCK: for (Node *n = node->body; n; n = n->next) gen(n); return; case ND_FUNCALL: { int nargs = 0; for (Node *arg = node->args; arg; arg = arg->next) { gen(arg); nargs++; } for (int i = nargs - 1; i >= 0; i--) printf(" pop %s\n", argreg[i]); // We need to align RSP to a 16 byte boundary before // calling a function because it is an ABI requirement. // RAX is set to 0 for variadic function. int seq = labelseq++; printf(" mov rax, rsp\n"); printf(" and rax, 15\n"); printf(" jnz .Lcall%d\n", seq); printf(" mov rax, 0\n"); printf(" call %s\n", node->funcname); printf(" jmp .Lend%d\n", seq); printf(".Lcall%d:\n", seq); printf(" sub rsp, 8\n"); printf(" mov rax, 0\n"); printf(" call %s\n", node->funcname); printf(" add rsp, 8\n"); printf(".Lend%d:\n", seq); printf(" push rax\n"); return; } case ND_RETURN: gen(node->lhs); printf(" pop rax\n"); printf(" jmp .Lreturn.%s\n", funcname); return; } gen(node->lhs); gen(node->rhs); printf(" pop rdi\n"); printf(" pop rax\n"); switch (node->kind) { case ND_ADD: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" add rax, rdi\n"); break; case ND_SUB: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" sub rax, rdi\n"); break; case ND_MUL: printf(" imul rax, rdi\n"); break; case ND_DIV: printf(" cqo\n"); printf(" idiv rdi\n"); break; case ND_EQ: printf(" cmp rax, rdi\n"); printf(" sete al\n"); printf(" movzb rax, al\n"); break; case ND_NE: printf(" cmp rax, rdi\n"); printf(" setne al\n"); printf(" movzb rax, al\n"); break; case ND_LT: printf(" cmp rax, rdi\n"); printf(" setl al\n"); printf(" movzb rax, al\n"); break; case ND_LE: printf(" cmp rax, rdi\n"); printf(" setle al\n"); printf(" movzb rax, al\n"); break; } printf(" push rax\n"); }
二項演算以外を行うアセンブリコードを生成する
switch (node->kind) { case ND_NULL: return; case ND_NUM: printf(" push %d\n", node->val); return; case ND_EXPR_STMT: gen(node->lhs); printf(" add rsp, 8\n"); return; case ND_VAR: gen_addr(node); if (node->ty->kind != TY_ARRAY) load(); return; case ND_ASSIGN: gen_lval(node->lhs); gen(node->rhs); store(); return; case ND_ADDR: gen_addr(node->lhs); return; case ND_DEREF: gen(node->lhs); if (node->ty->kind != TY_ARRAY) load(); return; case ND_IF: { int seq = labelseq++; if (node->els) { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lelse%d\n", seq); gen(node->then); printf(" jmp .Lend%d\n", seq); printf(".Lelse%d:\n", seq); gen(node->els); printf(".Lend%d:\n", seq); } else { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); gen(node->then); printf(".Lend%d:\n", seq); } return; } case ND_WHILE: { int seq = labelseq++; printf(".Lbegin%d:\n", seq); gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); gen(node->then); printf(" jmp .Lbegin%d\n", seq); printf(".Lend%d:\n", seq); return; } case ND_FOR: { int seq = labelseq++; if (node->init) gen(node->init); printf(".Lbegin%d:\n", seq); if (node->cond) { gen(node->cond); printf(" pop rax\n"); printf(" cmp rax, 0\n"); printf(" je .Lend%d\n", seq); } gen(node->then); if (node->inc) gen(node->inc); printf(" jmp .Lbegin%d\n", seq); printf(".Lend%d:\n", seq); return; } case ND_BLOCK: for (Node *n = node->body; n; n = n->next) gen(n); return; case ND_FUNCALL: { int nargs = 0; for (Node *arg = node->args; arg; arg = arg->next) { gen(arg); nargs++; } for (int i = nargs - 1; i >= 0; i--) printf(" pop %s\n", argreg[i]); // We need to align RSP to a 16 byte boundary before // calling a function because it is an ABI requirement. // RAX is set to 0 for variadic function. int seq = labelseq++; printf(" mov rax, rsp\n"); printf(" and rax, 15\n"); printf(" jnz .Lcall%d\n", seq); printf(" mov rax, 0\n"); printf(" call %s\n", node->funcname); printf(" jmp .Lend%d\n", seq); printf(".Lcall%d:\n", seq); printf(" sub rsp, 8\n"); printf(" mov rax, 0\n"); printf(" call %s\n", node->funcname); printf(" add rsp, 8\n"); printf(".Lend%d:\n", seq); printf(" push rax\n"); return; } case ND_RETURN: gen(node->lhs); printf(" pop rax\n"); printf(" jmp .Lreturn.%s\n", funcname); return; }
ノード型がND_VARの場合は(ノードがローカル変数に対応している場合は)、ノードの持つType構造体が配列型を表現していないことを確認してから、load関数を呼び出します。
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR58
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L58
case ND_VAR: gen_addr(node); if (node->ty->kind != TY_ARRAY) load(); return;
ノード型がND_ASSIGNの場合(ノードが等号記号に対応している場合)の処理において、gen_addr(node->lhs)からgen_lval(node->lhsへ変更します。
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR62
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L62
case ND_ASSIGN: gen_lval(node->lhs); gen(node->rhs); store(); return;
ノード型がND_DEREFの場合は(ノードが 間接参照演算子"*" に対応している場合は)、ノードの持つType構造体が配列型を表現していないことを確認してから、load関数を呼び出します。
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR71
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L71
case ND_DEREF: gen(node->lhs); if (node->ty->kind != TY_ARRAY) load(); return;
二項演算の対象となる値を得るためのアセンブリコードを生成する(変更なし)
gen(node->lhs); gen(node->rhs); printf(" pop rdi\n"); printf(" pop rax\n");
二項演算を行うアセンブリコードを生成する
switch (node->kind) { case ND_ADD: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" add rax, rdi\n"); break; case ND_SUB: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" sub rax, rdi\n"); break; case ND_MUL: printf(" imul rax, rdi\n"); break; case ND_DIV: printf(" cqo\n"); printf(" idiv rdi\n"); break; case ND_EQ: printf(" cmp rax, rdi\n"); printf(" sete al\n"); printf(" movzb rax, al\n"); break; case ND_NE: printf(" cmp rax, rdi\n"); printf(" setne al\n"); printf(" movzb rax, al\n"); break; case ND_LT: printf(" cmp rax, rdi\n"); printf(" setl al\n"); printf(" movzb rax, al\n"); break; case ND_LE: printf(" cmp rax, rdi\n"); printf(" setle al\n"); printf(" movzb rax, al\n"); break; } printf(" push rax\n"); }
ノード型がND_ADDの場合(加算を行う場合)における処理の改修です。
ノードの型が「~へのポインタ」型であるかどうかを、ノードが持つType構造体がさらにType構造体を参照しているかどうかで、判断するようにします。
ノードの型が「~へのポインタ」型である場合は、右オペランドの値をsize_of(node->ty->base)倍する必要があるので、加算を行うアセンブリコード"add rax, rdi"を生成する前に、アセンブリコード" imul rdi, (「~へのポインタ」型のサイズ)"を生成するようにします。
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR174
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L174
case ND_ADD: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" add rax, rdi\n"); break;
ノード型がND_SUBの場合(減算を行う場合)における処理の改修です。
ノードの型が「~へのポインタ」型であるかどうかを、ノードが持つType構造体がさらにType構造体を参照しているかどうかで、判断するようにします。
ノードの型が「~へのポインタ」型である場合は、右オペランドの値をsize_of(node->ty->base)倍する必要があるので、減算を行うアセンブリコード"sub rax, rdi"を生成する前に、アセンブリコード" imul rdi, (「~へのポインタ」型のサイズ)"を生成するようにします。
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR179
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L179
case ND_SUB: if (node->ty->base) printf(" imul rdi, %d\n", size_of(node->ty->base)); printf(" sub rax, rdi\n"); break;
gen_lval関数
https://github.com/rui314/chibicc/commit/f5536961e8182951376e320fafe4340c3a8e12b3#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR25
https://github.com/rui314/chibicc/blob/f5536961e8182951376e320fafe4340c3a8e12b3/codegen.c#L25
void gen_lval(Node *node) { if (node->ty->kind == TY_ARRAY) error_tok(node->tok, "not an lvalue"); gen_addr(node); }
gen_lval関数は、ノードが持つType構造体が配列型を表現していない場合にのみ、gen_addr関数を呼び出し左辺値を取得するためのアセンブリコードを生成するようにします。
追加・修正されたコンパイラのソースコード(ステップ22、配列の添字を扱えるコンパイラを作成する)
unary関数
https://github.com/rui314/chibicc/commit/c97aa80173b949104eb0a3e376bbf6309ce90d55#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR348
https://github.com/rui314/chibicc/blob/c97aa80173b949104eb0a3e376bbf6309ce90d55/parse.c#L348
// unary = ("+" | "-" | "*" | "&")? unary // | postfix Node *unary() { Token *tok; if (consume("+")) return unary(); if (tok = consume("-")) return new_binary(ND_SUB, new_num(0, tok), unary(), tok); if (tok = consume("&")) return new_unary(ND_ADDR, unary(), tok); if (tok = consume("*")) return new_unary(ND_DEREF, unary(), tok); return postfix(); }
unary関数は、生成規則 unary = ("+" | "-" | "*" | "&")? unary に基づいて、抽象構文木のノードを生成します。
「"+"または"-"または"&"または"*"を0回か1回」とunaray(変更なし)
Token *tok; if (consume("+")) return unary(); if (tok = consume("-")) return new_binary(ND_SUB, new_num(0, tok), unary(), tok); if (tok = consume("&")) return new_unary(ND_ADDR, unary(), tok); if (tok = consume("*")) return new_unary(ND_DEREF, unary(), tok);
postfix関数
https://github.com/rui314/chibicc/commit/c97aa80173b949104eb0a3e376bbf6309ce90d55#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR351
https://github.com/rui314/chibicc/blob/c97aa80173b949104eb0a3e376bbf6309ce90d55/parse.c#L351
// postfix = primary ("[" expr "]")* Node *postfix() { Node *node = primary(); Token *tok; while (tok = consume("[")) { // x[y] is short for *(x+y) Node *exp = new_binary(ND_ADD, node, expr(), tok); expect("]"); node = new_unary(ND_DEREF, exp, tok); } return node; }
postfix関数は、生成規則 postfix = primary ("[" expr "]")* に基づいて、抽象構文木のノードを生成します。
「"["、expr、"]"」を0回以上
Token *tok; while (tok = consume("[")) { // x[y] is short for *(x+y) Node *exp = new_binary(ND_ADD, node, expr(), tok); expect("]"); node = new_unary(ND_DEREF, exp, tok); } return node;
tok = consume("[")が真となる場合 → 現在読み取っているトークンが"["の場合は、以降のトークンの並びが、"配列のインデックスとなるexpr"、"]"となる想定でパースを進めていきます。その際、x[y]を*(x+y)と読み替えて、抽象構文木のノードを生成します。
追加・修正されたコンパイラのソースコード(ステップ20、sizeof演算子を扱えるコンパイラを作成する)
starts_with_reserved関数
https://github.com/rui314/chibicc/commit/30d530831fa2312ed6f2e73520c1525202745d88#diff-289479d6df6940b25dd31a6f2da4881331f916ec642bd1ae47d4ff0a365d8e88R131
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/tokenize.c#L131
char *starts_with_reserved(char *p) { // Keyword static char *kw[] = {"return", "if", "else", "while", "for", "int", "sizeof"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]); if (startswith(p, kw[i]) && !is_alnum(p[len])) return kw[i]; } // Multi-letter punctuator static char *ops[] = {"==", "!=", "<=", ">="}; for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++) if (startswith(p, ops[i])) return ops[i]; return NULL; }
キーワードを取得する
// Keyword static char *kw[] = {"return", "if", "else", "while", "for", "int", "sizeof"}; for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { int len = strlen(kw[i]); if (startswith(p, kw[i]) && !is_alnum(p[len])) return kw[i]; }
配列kwに"sizeof"を追加し、"sizeof"をキーワードとして扱えるようにします。
複数文字の記号を取得する(変更なし)
// Multi-letter punctuator static char *ops[] = {"==", "!=", "<=", ">="}; for (int i = 0; i < sizeof(ops) / sizeof(*ops); i++) if (startswith(p, ops[i])) return ops[i]; return NULL;
NodeKind
https://github.com/rui314/chibicc/commit/30d530831fa2312ed6f2e73520c1525202745d88#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R85
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/chibicc.h#L85
// AST node typedef enum { ND_ADD, // + ND_SUB, // - ND_MUL, // * ND_DIV, // / ND_EQ, // == ND_NE, // != ND_LT, // < ND_LE, // <= ND_ASSIGN, // = ND_ADDR, // unary & ND_DEREF, // unary * ND_RETURN, // "return" ND_IF, // "if" ND_WHILE, // "while" ND_FOR, // "for" ND_SIZEOF, // "sizeof" ND_BLOCK, // { ... } ND_FUNCALL, // Function call ND_EXPR_STMT, // Expression statement ND_VAR, // Variable ND_NUM, // Integer ND_NULL, // Empty statement } NodeKind;
キーワード"sizeof"を表現するノード型ND_SIZEOFを追加します。
primary関数
https://github.com/rui314/chibicc/commit/30d530831fa2312ed6f2e73520c1525202745d88#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR390
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/parse.c#L390
// primary = "(" expr ")" | "sizeof" unary | ident func-args? | num Node *primary() { Token *tok; if (consume("(")) { Node *node = expr(); expect(")"); return node; } if (tok = consume("sizeof")) return new_unary(ND_SIZEOF, unary(), tok); if (tok = consume_ident()) { if (consume("(")) { Node *node = new_node(ND_FUNCALL, tok); node->funcname = strndup(tok->str, tok->len); node->args = func_args(); return node; } Var *var = find_var(tok); if (!var) error_tok(tok, "undefined variable"); return new_var(var, tok); } tok = token; if (tok->kind != TK_NUM) error_tok(tok, "expected expression"); return new_num(expect_number(), tok); }
primary関数は、生成規則 primary = "(" expr ")" | "sizeof" unary | ident func-args? | num に基づいて、抽象構文木のノードを生成します。
"("、expr、")"(変更なし)
if (consume("(")) { Node *node = expr(); expect(")"); return node; }
sizeof、unary
if (tok = consume("sizeof")) return new_unary(ND_SIZEOF, unary(), tok);
tok = consume("sizeof")が真となる場合 → 現在着目トークンが"sizeof"の場合は、new_unary関数を呼び出し"sizeof"に対応するノードを生成します。
また、new_unary関数の第二引数にunary関数の戻り値を指定し、unary関数によって生成された抽象構文木のルートノードがsizeof演算子のオペランドに対応する子ノードとなるようにします。
ident、func-argsを0回か1回(変更なし)
if (tok = consume_ident()) { if (consume("(")) { Node *node = new_node(ND_FUNCALL, tok); node->funcname = strndup(tok->str, tok->len); node->args = func_args(); return node; } Var *var = find_var(tok); if (!var) error_tok(tok, "undefined variable"); return new_var(var, tok); }
num(変更なし)
tok = token; if (tok->kind != TK_NUM) error_tok(tok, "expected expression"); return new_num(expect_number(), tok);
visit関数
https://github.com/rui314/chibicc/commit/30d530831fa2312ed6f2e73520c1525202745d88#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R91
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/type.c#L91
void visit(Node *node) { if (!node) return; visit(node->lhs); visit(node->rhs); visit(node->cond); visit(node->then); visit(node->els); visit(node->init); visit(node->inc); for (Node *n = node->body; n; n = n->next) visit(n); for (Node *n = node->args; n; n = n->next) visit(n); switch (node->kind) { case ND_MUL: case ND_DIV: case ND_EQ: case ND_NE: case ND_LT: case ND_LE: case ND_FUNCALL: case ND_NUM: node->ty = int_type(); return; case ND_VAR: node->ty = node->var->ty; return; case ND_ADD: if (node->rhs->ty->base) { Node *tmp = node->lhs; node->lhs = node->rhs; node->rhs = tmp; } if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return; case ND_SUB: if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return; case ND_ASSIGN: node->ty = node->lhs->ty; return; case ND_ADDR: if (node->lhs->ty->kind == TY_ARRAY) node->ty = pointer_to(node->lhs->ty->base); else node->ty = pointer_to(node->lhs->ty); return; case ND_DEREF: if (!node->lhs->ty->base) error_tok(node->tok, "invalid pointer dereference"); node->ty = node->lhs->ty->base; return; case ND_SIZEOF: node->kind = ND_NUM; node->ty = int_type(); node->val = size_of(node->lhs->ty); node->lhs = NULL; return; } }
ノードがセットされていない場合(変更なし)
if (!node) return;
全ての子のノードに対してvisit関数を再帰的に呼び出す(変更なし)
visit(node->lhs); visit(node->rhs); visit(node->cond); visit(node->then); visit(node->els); visit(node->init); visit(node->inc); for (Node *n = node->body; n; n = n->next) visit(n); for (Node *n = node->args; n; n = n->next) visit(n);
ノード型に応じて型付け処理を行う(int型を付ける、変更なし)
switch (node->kind) { case ND_MUL: case ND_DIV: case ND_EQ: case ND_NE: case ND_LT: case ND_LE: case ND_FUNCALL: case ND_NUM: node->ty = int_type(); return;
ノード型に応じて型付け処理を行う(ND_VAR、変更なし)
case ND_VAR: node->ty = node->var->ty; return;
ノード型に応じて型付け処理を行う(ND_ADD、変更なし)
case ND_ADD: if (node->rhs->ty->base) { Node *tmp = node->lhs; node->lhs = node->rhs; node->rhs = tmp; } if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return;
ノード型に応じて型付け処理を行う(ND_SUB、変更なし)
case ND_SUB: if (node->rhs->ty->base) error_tok(node->tok, "invalid pointer arithmetic operands"); node->ty = node->lhs->ty; return;
ノード型に応じて型付け処理を行う(ND_ASSIGN、変更なし)
case ND_ASSIGN: node->ty = node->lhs->ty; return;
ノード型に応じて型付け処理を行う(ND_ADDR、変更なし)
case ND_ADDR: if (node->lhs->ty->kind == TY_ARRAY) node->ty = pointer_to(node->lhs->ty->base); else node->ty = pointer_to(node->lhs->ty); return;
ノード型に応じて型付け処理を行う(ND_DEREF、変更なし)
case ND_DEREF: if (!node->lhs->ty->base) error_tok(node->tok, "invalid pointer dereference"); node->ty = node->lhs->ty->base; return;
ノード型に応じて型付け処理を行う(ND_SIZEOF)
case ND_SIZEOF: node->kind = ND_NUM; node->ty = int_type(); node->val = size_of(node->lhs->ty); node->lhs = NULL; return;
nodeのノード型がND_SIZEOFの場合は、nodeをsizeof演算子の評価値に対応するノードへ変更します。
nodeのノード型をND_NUMに変更し、nodeの持つ型をint型にするためにint_type関数を呼び出します。
nodeの持つ値は、子ノードnode->lhsの持つ型のサイズとなります。
最後に、子ノードへの参照をなくしておきます。
テストコード
https://github.com/rui314/chibicc/commit/30d530831fa2312ed6f2e73520c1525202745d88#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R136
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/test.sh#L136
#!/bin/bash cat <<EOF | gcc -xc -c -o tmp2.o - int ret3() { return 3; } int ret5() { return 5; } int add(int x, int y) { return x+y; } int sub(int x, int y) { return x-y; } int add6(int a, int b, int c, int d, int e, int f) { return a+b+c+d+e+f; } EOF assert() { expected="$1" input="$2" ./chibicc "$input" > tmp.s gcc -static -o tmp tmp.s tmp2.o ./tmp actual="$?" if [ "$actual" = "$expected" ]; then echo "$input => $actual" else echo "$input => $expected expected, but got $actual" exit 1 fi } assert 0 'int main() { return 0; }' assert 42 'int main() { return 42; }' assert 21 'int main() { return 5+20-4; }' assert 41 'int main() { return 12 + 34 - 5 ; }' assert 47 'int main() { return 5+6*7; }' assert 15 'int main() { return 5*(9-6); }' assert 4 'int main() { return (3+5)/2; }' assert 10 'int main() { return -10+20; }' assert 10 'int main() { return - -10; }' assert 10 'int main() { return - - +10; }' assert 0 'int main() { return 0==1; }' assert 1 'int main() { return 42==42; }' assert 1 'int main() { return 0!=1; }' assert 0 'int main() { return 42!=42; }' assert 1 'int main() { return 0<1; }' assert 0 'int main() { return 1<1; }' assert 0 'int main() { return 2<1; }' assert 1 'int main() { return 0<=1; }' assert 1 'int main() { return 1<=1; }' assert 0 'int main() { return 2<=1; }' assert 1 'int main() { return 1>0; }' assert 0 'int main() { return 1>1; }' assert 0 'int main() { return 1>2; }' assert 1 'int main() { return 1>=0; }' assert 1 'int main() { return 1>=1; }' assert 0 'int main() { return 1>=2; }' assert 3 'int main() { int a; a=3; return a; }' assert 8 'int main() { int a; int z; a=3; z=5; return a+z; }' assert 3 'int main() { int a=3; return a; }' assert 8 'int main() { int a=3; int z=5; return a+z; }' assert 1 'int main() { return 1; 2; 3; }' assert 2 'int main() { 1; return 2; 3; }' assert 3 'int main() { 1; 2; return 3; }' assert 3 'int main() { int foo=3; return foo; }' assert 8 'int main() { int foo123=3; int bar=5; return foo123+bar; }' assert 3 'int main() { if (0) return 2; return 3; }' assert 3 'int main() { if (1-1) return 2; return 3; }' assert 2 'int main() { if (1) return 2; return 3; }' assert 2 'int main() { if (2-1) return 2; return 3; }' assert 3 'int main() { {1; {2;} return 3;} }' assert 10 'int main() { int i=0; i=0; while(i<10) i=i+1; return i; }' assert 55 'int main() { int i=0; int j=0; while(i<=10) {j=i+j; i=i+1;} return j; }' assert 55 'int main() { int i=0; int j=0; for (i=0; i<=10; i=i+1) j=i+j; return j; }' assert 3 'int main() { for (;;) return 3; return 5; }' assert 3 'int main() { return ret3(); }' assert 5 'int main() { return ret5(); }' assert 8 'int main() { return add(3, 5); }' assert 2 'int main() { return sub(5, 3); }' assert 21 'int main() { return add6(1,2,3,4,5,6); }' assert 32 'int main() { return ret32(); } int ret32() { return 32; }' assert 7 'int main() { return add2(3,4); } int add2(int x, int y) { return x+y; }' assert 1 'int main() { return sub2(4,3); } int sub2(int x, int y) { return x-y; }' assert 55 'int main() { return fib(9); } int fib(int x) { if (x<=1) return 1; return fib(x-1) + fib(x-2); }' assert 3 'int main() { int x=3; return *&x; }' assert 3 'int main() { int x=3; int *y=&x; int **z=&y; return **z; }' assert 5 'int main() { int x=3; int y=5; return *(&x+1); }' assert 5 'int main() { int x=3; int y=5; return *(1+&x); }' assert 3 'int main() { int x=3; int y=5; return *(&y-1); }' assert 5 'int main() { int x=3; int y=5; int *z=&x; return *(z+1); }' assert 3 'int main() { int x=3; int y=5; int *z=&y; return *(z-1); }' assert 5 'int main() { int x=3; int *y=&x; *y=5; return x; }' assert 7 'int main() { int x=3; int y=5; *(&x+1)=7; return y; }' assert 7 'int main() { int x=3; int y=5; *(&y-1)=7; return x; }' assert 8 'int main() { int x=3; int y=5; return foo(&x, y); } int foo(int *x, int y) { return *x + y; }' assert 3 'int main() { int x[2]; int *y=&x; *y=3; return *x; }' assert 3 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *x; }' assert 4 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+1); }' assert 5 'int main() { int x[3]; *x=3; *(x+1)=4; *(x+2)=5; return *(x+2); }' assert 0 'int main() { int x[2][3]; int *y=x; *y=0; return **x; }' assert 1 'int main() { int x[2][3]; int *y=x; *(y+1)=1; return *(*x+1); }' assert 2 'int main() { int x[2][3]; int *y=x; *(y+2)=2; return *(*x+2); }' assert 3 'int main() { int x[2][3]; int *y=x; *(y+3)=3; return **(x+1); }' assert 4 'int main() { int x[2][3]; int *y=x; *(y+4)=4; return *(*(x+1)+1); }' assert 5 'int main() { int x[2][3]; int *y=x; *(y+5)=5; return *(*(x+1)+2); }' assert 6 'int main() { int x[2][3]; int *y=x; *(y+6)=6; return **(x+2); }' assert 3 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *x; }' assert 4 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+1); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; x[2]=5; return *(x+2); }' assert 5 'int main() { int x[3]; *x=3; x[1]=4; 2[x]=5; return *(x+2); }' assert 0 'int main() { int x[2][3]; int *y=x; y[0]=0; return x[0][0]; }' assert 1 'int main() { int x[2][3]; int *y=x; y[1]=1; return x[0][1]; }' assert 2 'int main() { int x[2][3]; int *y=x; y[2]=2; return x[0][2]; }' assert 3 'int main() { int x[2][3]; int *y=x; y[3]=3; return x[1][0]; }' assert 4 'int main() { int x[2][3]; int *y=x; y[4]=4; return x[1][1]; }' assert 5 'int main() { int x[2][3]; int *y=x; y[5]=5; return x[1][2]; }' assert 6 'int main() { int x[2][3]; int *y=x; y[6]=6; return x[2][0]; }' assert 8 'int main() { int x; return sizeof(x); }' assert 8 'int main() { int x; return sizeof x; }' assert 8 'int main() { int *x; return sizeof(x); }' assert 32 'int main() { int x[4]; return sizeof(x); }' assert 96 'int main() { int x[3][4]; return sizeof(x); }' assert 32 'int main() { int x[3][4]; return sizeof(*x); }' assert 8 'int main() { int x[3][4]; return sizeof(**x); }' assert 9 'int main() { int x[3][4]; return sizeof(**x) + 1; }' assert 9 'int main() { int x[3][4]; return sizeof **x + 1; }' assert 8 'int main() { int x[3][4]; return sizeof(**x + 1); }' echo OK
Makefile
https://github.com/rui314/chibicc/blob/30d530831fa2312ed6f2e73520c1525202745d88/Makefile
CFLAGS=-std=c11 -g -static SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) chibicc: $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(OBJS): chibicc.h test: chibicc ./test.sh clean: rm -f chibicc *.o *~ tmp* .PHONY: test clean