chibiccを読む~Cコンパイラコードリーディング~ ステップ2
トップページ
jupiteroak.hatenablog.com
「低レイヤを知りたい人のためのCコンパイラ作成入門」のCコンパイラを読んでいきます。
www.sigbus.info
ステップ2に該当
github.com
コンパイラのソースコード
main.c
https://github.com/rui314/chibicc/commit/afc9e8f05faddf051aa3a578520d6484ab451282#diff-a0cb465674c1b01a07d361f25a0ef2b0214b7dfe9412b7777f89add956da10ecR10
https://github.com/rui314/chibicc/blob/afc9e8f05faddf051aa3a578520d6484ab451282/main.c#L10
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "%s: invalid number of arguments\n", argv[0]); return 1; } char *p = argv[1]; printf(".intel_syntax noprefix\n"); printf(".global main\n"); printf("main:\n"); printf(" mov rax, %ld\n", strtol(p, &p, 10)); while (*p) { if (*p == '+') { p++; printf(" add rax, %ld\n", strtol(p, &p, 10)); continue; } if (*p == '-') { p++; printf(" sub rax, %ld\n", strtol(p, &p, 10)); continue; } fprintf(stderr, "unexpected character: '%c'\n", *p); return 1; } printf(" ret\n"); return 0; }
コマンドライン引数の個数をチェックする(変更なし)
if (argc != 2) { fprintf(stderr, "%s: invalid number of arguments\n", argv[0]); return 1; }
アセンブリコードを生成する
char *p = argv[1]; printf(".intel_syntax noprefix\n"); printf(".global main\n"); printf("main:\n"); printf(" mov rax, %ld\n", strtol(p, &p, 10)); while (*p) { if (*p == '+') { p++; printf(" add rax, %ld\n", strtol(p, &p, 10)); continue; } if (*p == '-') { p++; printf(" sub rax, %ld\n", strtol(p, &p, 10)); continue; } fprintf(stderr, "unexpected character: '%c'\n", *p); return 1; } printf(" ret\n"); return 0;
strtol関数について
strtol関数は、第一引数で指定された文字列の先頭部分にある数字を、第三引数で指定された基数で、取得します。
第二引数のアドレスが指定するポインタには、第一引数で指定された文字列の先頭部分にある数字の次にある文字を指定するアドレスが格納されます。
char *p = "5+20-4"; strtol(p, &p, 10));
例えば上記のようなプログラムの場合、第一引数pで指定された文字列"5+20-4"の先頭部分にある数字"5"を、第三引数で指定された基数(10進数)で、取得します(strtol関数の戻り値が5になります)。
第二引数のアドレス&pが指定するポインタpには、第一引数で指定された文字列"5+20-4"の先頭部分にある数字"5"の次にある文字"+"を指定するアドレスが格納されます。
コンパイラが出力するアセンブリコード
「5+20-4」を入力から読んだ場合
.intel_syntax noprefix .globl main main: mov rax, 5 add rax, 20 sub rax, 4 ret
テストコード
test.sh
https://github.com/rui314/chibicc/commit/afc9e8f05faddf051aa3a578520d6484ab451282#diff-3722d9ba8feb2d3feac8ce71a209a638d4b404e1c53f937188761181594023e2R21
https://github.com/rui314/chibicc/blob/afc9e8f05faddf051aa3a578520d6484ab451282/test.sh#L21
#!/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 assert 21 '5+20-4' echo OK
Makefile
https://github.com/rui314/chibicc/blob/afc9e8f05faddf051aa3a578520d6484ab451282/Makefile
CFLAGS=-std=c11 -g -static chibicc: main.o $(CC) -o $@ $? $(LDFLAGS) test: chibicc ./test.sh clean: rm -f chibicc *.o *~ tmp* .PHONY: test clean