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

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

今回作成するコンパイラ

整数1個をコンパイルする言語の作成(「1個の数」を入力から読んで「その数をプログラムの終了コードとして終了するアセンブリコード」を出力するコンパイラを作成する)

コンパイラソースコード

main.c
https://github.com/rui314/chibicc/commit/f722daaaae0606115df4ace5a852da23c1a5b0f3#diff-a0cb465674c1b01a07d361f25a0ef2b0214b7dfe9412b7777f89add956da10ecR1
https://github.com/rui314/chibicc/blob/f722daaaae0606115df4ace5a852da23c1a5b0f3/main.c#L1

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  if (argc != 2) {
    fprintf(stderr, "引数の個数が正しくありません\n");
    return 1;
  }

  printf(".intel_syntax noprefix\n");
  printf(".globl main\n");
  printf("main:\n");
  printf("  mov rax, %d\n", atoi(argv[1]));
  printf("  ret\n");
  return 0;
}

コマンドライン引数の個数をチェックする


argcとargvについて
argcはコマンドライン引数の個数を表す値です。argvはコマンドライン引数配列(コマンドライン引数を格納するための配列)で、argv[0]には実行されたプログラム名の文字列、argv[1]以降にはプログラムを実行するときに入力されたコマンドライン引数の文字列が格納されています。
例えば、コマンドラインに「 ./chibicc 123」と入力してプログラムchibiccを実行すると、 argcの値は2となり、argv[0]には"./chibicc"、argv[1]には"123"が格納されます。


fprintf関数について
fprintf関数は、第一引数のファイルポインタが指定するファイルに、第二引数のchar型へのポインタが指定する文字列データを書き込みます。
今回の場合は、標準エラー出力ファイルに、文字列データ"引数の個数が正しくありません\n"が書き込まれるので、このfprintf関数が実行されるとコンソール画面に「引数の個数が正しくありません」と表示されます。

アセンブリコードを生成する


.intel_syntax noprefix について
gas(GNU アセンブラ)はAT&T記法のx86アセンブリ言語アセンブルするアセンブラですが、.intel_syntaxディレクティブを使用することでIntel記法のx86アセンブリ言語アセンブルできるようになります。さらに、noprefixを指定することでAT&T記法に特有のプレフェックス(%や$など)を記入する必要がなくなります。
https://www.gnu.org/software/binutils/ → documentation for binutils 2.39 → Assembler (gas) → 9.16.3.1 AT&T Syntax versus Intel Syntax


.globl について
globlディレクティブによって、mainラベルが外部のファイルからでも参照できるようになります。


main:
mov命令が配置されているアドレスにmainラベル(アドレスの別名)を付けています。


mov rax, argv[1]の値
mov命令は、第二オペランドから第一オペランドへデータをコピーします。
ここでは、raxレジスタへatoi関数によって変換されたコマンドライン引数の数値をコピーしています。


ret
ret命令は、サブルーチンの呼び出し元へ制御を戻す命令です。
ここでは、main関数の呼び出し元へ制御を移しています(main関数の処理を終了しています)。

コンパイラが出力するアセンブリコード

コマンドライン引数が42の場合

.intel_syntax noprefix
.globl main

main:
        mov rax, 42
        ret

テストコード

test.sh
https://github.com/rui314/chibicc/commit/f722daaaae0606115df4ace5a852da23c1a5b0f3#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R1
https://github.com/rui314/chibicc/blob/f722daaaae0606115df4ace5a852da23c1a5b0f3/test.sh#L1

#!/bin/bash
assert() {
  expected="$1"
  input="$2"

  ./chibicc "$input" > tmp.s
  gcc -static -o tmp tmp.s
  ./tmp
  actual="$?"

  if [ "$actual" = "$expected" ]; then
    echo "$input => $actual"
  else
    echo "$input => $expected expected, but got $actual"
    exit 1
  fi
}

assert 0 0
assert 42 42

echo OK