chibiccを読む~Cコンパイラコードリーディング~ ステップ23

トップページ
jupiteroak.hatenablog.com
「低レイヤを知りたい人のためのCコンパイラ作成入門」のCコンパイラを読んでいきます。
www.sigbus.info
ステップ23に該当
github.com

今回作成するコンパイラ

グローバル変数を実装する(グローバル変数を扱えるコンパイラを作成する)

追加・修正されたコンパイラソースコード

main関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a0cb465674c1b01a07d361f25a0ef2b0214b7dfe9412b7777f89add956da10ecR10
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/main.c#L10

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();
  Program *prog = program();
  add_type(prog);

  // Assign offsets to local variables.
  for (Function *fn = prog->fns; 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();
パースを行う
  Program *prog = program();

Function構造体からProgram構造体へ更新します。

型付けを行う(変更なし)
  add_type(prog);
引数・ローカル変数のアドレスとスタックサイズを定める
  // Assign offsets to local variables.
  for (Function *fn = prog->fns; 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;
  }

Program構造体progからFunction構造体fnを取得するようにします。

アセンブリコードを生成する(変更なし)
  // Traverse the AST to emit assembly.
  codegen(prog);

  return 0;

program関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR97
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L97

// program = (global-var | function)*
Program *program() {
  Function head;
  head.next = NULL;
  Function *cur = &head;
  globals = NULL;

  while (!at_eof()) {
    if (is_function()) {
      cur->next = function();
      cur = cur->next;
    } else {
      global_var();
    }
  }

  Program *prog = calloc(1, sizeof(Program));
  prog->globals = globals;
  prog->fns = head.next;
  return prog;
}
Program構造体からなる連結リストのヘッダーを作成する
  Function head;
  head.next = NULL;
  Function *cur = &head;
  globals = NULL;
Program構造体からなる連結リストを作成する
  while (!at_eof()) {
    if (is_function()) {
      cur->next = function();
      cur = cur->next;
    } else {
      global_var();
    }
  }

is_function関数の戻り値がtrueとなる場合は関数定義のパースを行うようにし、is_function関数の戻り値がfalseとなる場合はグローバル変数のパースを行うようにします。

Program構造体をリターンする
  Program *prog = calloc(1, sizeof(Program));
  prog->globals = globals;
  prog->fns = head.next;
  return prog;

Program構造体を生成し、グローバル変数を管理しているValist構造体の連結リストのアドレス、抽象構文木のルートノードからなる連結リスト(1つの関数定義が1つの抽象構文木に対応している)のアドレスをセットします。

is_function関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR89
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L89

bool is_function() {
  Token *tok = token;
  basetype();
  bool isfunc = consume_ident() && consume("(");
  token = tok;
  return isfunc;
}

is_function関数は、トークン列を先読みして、そのトークン列が関数定義の構文の一部かグローバル変数の構文の一部かを判定します。


Token *tok = token;
現在着目しているトークンを指定するアドレスをtokに保存しておきます。


basetype();
basetype関数を呼び出して、型となるキーワードを表現しているトークンを読み取ります。


bool isfunc = consume_ident() && consume("(");
consume_ident()の戻り値とconsume("(")の戻り値の論理積の結果を取得します。
consume_ident()を呼び出して識別子を表現したトークンを取得できた、かつ、consume("(")を呼び出して"("を表現したトークンを取得できた場合に、isfuncはtrueとなります(そのトークン列が関数定義の構文の一部であると判断します)。


token = tok;
現在着目しているトークンをis_function関数内でトークン列を読み進める前の状態に戻しておきます。


return isfunc
判定結果を戻り値としてリターンします。

read_func_param関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR143
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L143

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, true);
  return vl;
}
Type構造体を生成する(変更なし)
  Type *ty = basetype();
変数名か配列名になる識別子を取得する(変更なし)
  char *name = expect_ident();
Type構造体を更新する(変更なし)
  ty = read_type_suffix(ty);
VarList構造体を生成する
  VarList *vl = calloc(1, sizeof(VarList));
  vl->var = push_var(name, ty, true);

push_var関数の第三引数にtrueを指定して、ローカル変数を表現したVar構造体を生成するようにします。

Var構造体を生成する(変更なし)
 return vl;

declaration関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR204
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L204

// 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, true);

  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);
}
basetype(変更なし)
  Type *ty = basetype();
ident(変更なし)
  char *name = expect_ident();
「"["とnumと"]"」を0回以上
  ty = read_type_suffix(ty);
  Var *var = push_var(name, ty, true);

push_var関数の第三引数にtrueを指定して、ローカル変数を表現した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);

global_var関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR189
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L189

// global-var = basetype ident ("[" num "]")* ";"
void global_var() {
  Type *ty = basetype();
  char *name = expect_ident();
  ty = read_type_suffix(ty);
  expect(";");
  push_var(name, ty, false);
}

global_var関数は、生成規則 global-var = basetype ident ("[" num "]")* ";" に基づいて、抽象構文木のノードを生成します。

basetype
  Type *ty = basetype();

basetype関数を呼び出して、グローバル変数の型を表現したトークンを読み取ってからType構造体を生成します。

ident
  char *name = expect_ident();

この時点では、トークンから読み取った識別子が配列名か変数名かわからないので、expect_ident関数を呼び出しその識別子を保存しておきます。

「"["、num、"]"」を0回以上
  ty = read_type_suffix(ty);

read_type_suffix関数を呼び出して、トークンを読み取りながら、グローバル変数の型が、配列型か、それ以外の型か、を決定します。

";"
 expect(";");
 push_var(name, ty, false);

expect(";")を呼び出して、現在着目しているトークンが";"であることを確認します。
最後に、配列名か変数名をグルーバル変数を表現するVar構造体を生成します。

Var構造体

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-d06dbb7ef5899cdf50b340464444680b13aded45363e7aba944dc3551fdf6334R57
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/chibicc.h#L57

// Variable
typedef struct Var Var;
struct Var {
  char *name;    // Variable name
  Type *ty;      // Type
  bool is_local; // local or global

  // Local variable
  int offset;    // Offset from RBP
};

Var構造体がローカル変数を表現しているのかグローバル変数を表現しているのかを区別するために、is_localメンバを追加します。

push_var関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR62
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L63

Var *push_var(char *name, Type *ty, bool is_local) {
  Var *var = calloc(1, sizeof(Var));
  var->name = name;
  var->ty = ty;
  var->is_local = is_local;

  VarList *vl = calloc(1, sizeof(VarList));
  vl->var = var;

  if (is_local) {
    vl->next = locals;
    locals = vl;
  } else {
    vl->next = globals;
    globals = vl;
  }

  return var;
}
Var構造体を生成する
  Var *var = calloc(1, sizeof(Var));
  var->name = name;
  var->ty = ty;
  var->is_local = is_local;

生成したVar構造体に、ローカル変数かグローバル変数かを示すis_localフラグをセットします。

変数を管理しているValist構造体を生成する
  VarList *vl = calloc(1, sizeof(VarList));
  vl->var = var;

  if (is_local) {
    vl->next = locals;
    locals = vl;
  } else {
    vl->next = globals;
    globals = vl;
  }

is_localがtrueの場合 → Var構造体がローカル変数を表現している場合は、localsが指定する連結リストにVarList構造体を追加します。
is_localがfalseの場合 → Var構造体がグローバル変数を表現している場合は、globalsが指定する連結リストにVarList構造体を追加します。

生成したVar構造体をリターンする(変更なし)
  return var;

find_var関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6aR14
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/parse.c#L14

// Find a local variable by name.
Var *find_var(Token *tok) {
  for (VarList *vl = locals; vl; vl = vl->next) {
    Var *var = vl->var;
    if (strlen(var->name) == tok->len && !memcmp(tok->str, var->name, tok->len))
      return var;
  }

  for (VarList *vl = globals; vl; vl = vl->next) {
    Var *var = vl->var;
    if (strlen(var->name) == tok->len && !memcmp(tok->str, var->name, tok->len))
      return var;
  }
  return NULL;
}

globalsが指定する連結リストからもVar構造体を探索するようにします。

add_type関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-76824d44018db869c0d65b9ae798b19e16fca3176fd792fbc855fe74da032e32R100
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/type.c#L100

void add_type(Program *prog) {
  for (Function *fn = prog->fns; fn; fn = fn->next)
    for (Node *node = fn->node; node; node = node->next)
      visit(node);
}

構造体変数progの型をFunctionからProgramへ変更します。

codegen関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR263
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/codegen.c#L263

void codegen(Program *prog) {
  printf(".intel_syntax noprefix\n");
  emit_data(prog);
  emit_text(prog);
}
ディレクティブを生成する
printf(".intel_syntax noprefix\n");
dataセクションのアセンブリコードを生成する
emit_data(prog);

emit_data関数を呼び出して、data領域に配置させるアセンブリコードを生成します。

textセクションのアセンブリコードを生成する
emit_text(prog);

emit_text関数を呼び出して、text領域に配置させるアセンブリコードを生成します。

emit_data関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR221
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/codegen.c#L221

void emit_data(Program *prog) {
  printf(".data\n");

  for (VarList *vl = prog->globals; vl; vl = vl->next) {
    Var *var = vl->var;
    printf("%s:\n", var->name);
    printf("  .zero %d\n", size_of(var->ty));
  }
}

emit_data関数は、data領域に配置させるアセンブリコードを生成します。

データセクションを指定するディレクティブを生成する
  printf(".data\n");

data領域の開始を示す.dataディレクティブを生成します。

グローバル変数のラベルを生成する
  for (VarList *vl = prog->globals; vl; vl = vl->next) {
    Var *var = vl->var;
    printf("%s:\n", var->name);

グローバル変数名を使ったラベルを生成します。

グローバル変数の値を初期化する
  printf("  .zero %d\n", size_of(var->ty));
  }

.zero n ディレクティブによって、nバイト分の領域を0で初期化します。

emit_text関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR231
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/codegen.c#L231

void emit_text(Program *prog) {
  printf(".text\n");

  for (Function *fn = prog->fns; fn; fn = fn->next) {
    printf(".global %s\n", fn->name);
    printf("%s:\n", fn->name);
    funcname = fn->name;

    // Prologue
    printf("  push rbp\n");
    printf("  mov rbp, rsp\n");
    printf("  sub rsp, %d\n", fn->stack_size);

    // Push arguments to the stack
    int i = 0;
    for (VarList *vl = fn->params; vl; vl = vl->next) {
      Var *var = vl->var;
      printf("  mov [rbp-%d], %s\n", var->offset, argreg[i++]);
    }

    // Emit code
    for (Node *node = fn->node; node; node = node->next)
      gen(node);

    // Epilogue
    printf(".Lreturn.%s:\n", funcname);
    printf("  mov rsp, rbp\n");
    printf("  pop rbp\n");
    printf("  ret\n");
  }
}

emit_text関数は、text領域に配置させるアセンブリコードを生成します。

テキストセクションを指定するディレクティブを生成する
  printf(".text\n");

text領域の開始を示す.textディレクティブを生成します。

関数名のラベルを生成する
  for (Function *fn = prog->fns; fn; fn = fn->next) {
    printf(".global %s\n", fn->name);
    printf("%s:\n", fn->name);
    funcname = fn->name;

.globalディレクティブと関数名のラベルを生成します。

プロローグとなるアセンブリコードを生成する
    // Prologue
    printf("  push rbp\n");
    printf("  mov rbp, rsp\n");
    printf("  sub rsp, %d\n", fn->stack_size);
引数の値をスタックに退避させるアセンブリコードを生成する
    // Push arguments to the stack
    int i = 0;
    for (VarList *vl = fn->params; vl; vl = vl->next) {
      Var *var = vl->var;
      printf("  mov [rbp-%d], %s\n", var->offset, argreg[i++]);
    }
関数の実装に対応するアセンブリコードを生成する
    // Emit code
    for (Node *node = fn->node; node; node = node->next)
      gen(node);
エピローグとなるアセンブリコードを生成する
    // Epilogue
    printf(".Lreturn.%s:\n", funcname);
    printf("  mov rsp, rbp\n");
    printf("  pop rbp\n");
    printf("  ret\n");

gen_addr関数

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-629fe11334ae1d560032cdb6cc6f9a4fbb0f5b1365894b6b648d6ee4d5a654beR13
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/codegen.c#L13

// Pushes the given node's address to the stack.
void gen_addr(Node *node) {
  switch (node->kind) {
  case ND_VAR: {
    Var *var = node->var;
    if (var->is_local) {
      printf("  lea rax, [rbp-%d]\n", var->offset);
      printf("  push rax\n");
    } else {
      printf("  push offset %s\n", var->name);
    }
    return;
  }
  case ND_DEREF:
    gen(node->lhs);
    return;
  }

  error_tok(node->tok, "not an lvalue");
}
左辺値を取得するためのアセンブリコードを生成する
  switch (node->kind) {
  case ND_VAR: {
    Var *var = node->var;
    if (var->is_local) {
      printf("  lea rax, [rbp-%d]\n", var->offset);
      printf("  push rax\n");
    } else {
      printf("  push offset %s\n", var->name);
    }
    return;
  }
  case ND_DEREF:
    gen(node->lhs);
    return;
  }

ノード型がND_VARの場合(ノードがローカル変数を表現している場合)における処理の改修です。
var->is_localがfalseになる場合 → Var構造体がグローバル変数を表現している場合は、グローバル変数が配置されているアドレスをスタックに退避させるアセンブリコード "push offset グローバル変数名"を生成します。アセンブリコードにあるoffset演算子は変数名からアドレスを取得します。

エラーメッセージを表示する(変更なし)
  error_tok(node->tok, "not an lvalue");

テストコード

https://github.com/rui314/chibicc/commit/97935ca2fadd1fe61e81b98cc0e528781a7ec868#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R147
https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/test.sh#L147

#!/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); }'

assert 0 'int x; int main() { return x; }'
assert 3 'int x; int main() { x=3; return x; }'
assert 0 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[0]; }'
assert 1 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[1]; }'
assert 2 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[2]; }'
assert 3 'int x[4]; int main() { x[0]=0; x[1]=1; x[2]=2; x[3]=3; return x[3]; }'

assert 8 'int x; int main() { return sizeof(x); }'
assert 32 'int x[4]; int main() { return sizeof(x); }'

echo OK

Makefile

https://github.com/rui314/chibicc/blob/97935ca2fadd1fe61e81b98cc0e528781a7ec868/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