hcc
Cコンパイラの作り方
学びたいこと
Haskellに慣れる
というか関数型言語に慣れる
アセンブリ言語
最適化
サブプロジェクト
方針
9ccとtigerが軸
実行の仕方
haskellコードを編集後、実際にアセンブリを出力してから実行する場合
$ docker build -t hcc .
$ docker run -v "$PWD"/.:/home --rm -ti hcc
起動しておく
$ stack build
$ stack exec hcc-exe "(3 + 4) - 3 * 5 / 4;" > tmp.s
セミコロンは必須だよ!!!!
# gcc -g main.c tmp.s -o tmp
# ./tmp
# echo $?
gdbを使ってアセンブリのデバッグをする
上の # gcc -g main.c tmp.s -o tmpを実行するところまでは同じ
# gdb ./tmpでgdbを起動する
Haskell上でASTを確認する場合
src内でghciを起動して、:l Parser.hsで読み込む
run program "1+2"で実行するとASTが得られる
ちなみに:rでリロード
テスト
parserレベルのテスト
$ stack test
アセンブリレベルのテスト
$ stack build
ホスト側
$ cd test/tests
$ ./test.sh
ゲスト側
$ ./test.sh
DockerのHaskellのイメージがでかすぎるので、断念
↑これはtest.shの実行に影響する
つまり、test.shの中で$ runhaskell main.hs 42 > tmp.sのようなものが実行できない
なので、テストのやり方を自分で考えないといけない
具体的には、コード内で単体テストを行う
参考
HaskellとLLVMでJITコンパイラを作る
メモ
ASTを作れるようになったので、ここからAsmに変換する
BNFの書き方
関数型言語で実装するか、手続き型で実装するかで、適切な表記法が変わる?
前者の場合再帰で
後者の場合クリーネ閉包*で
todo
型をもう少し分類したい
parserはもっと洗練できる
chainl1を理解しよう
Either Monadを導入しよう
右結合を解消しよう
gen関数をもっと洗練しよう
重複が多い
テストケースを増やす
エラー処理をしたい
Exceptモナドを使う
ハードコーディングしてるので修正
Parsecがエラーを吐いてもtmp.sに書き込まれるの意味ないのでやめたい
assignの左結合か右結合かみたいなところをチェック
リターンアドレス、ベースポインタの復習
★READMEへの追記
features
四則演算
local var
など
テストの仕方
description
C Compiler in Haskell with Parsec的な
genの方もテストしたい..
'3;'のようにただの数値の時おかしい
NG時のテスト
codeGenのリファクタ!!!!!!
codeGenのfor文のNop, 0, 1のときの処理
↓これの「最高のMonadチュートリアル」のところから読む
↓この辺、どっか別の場所に移す
CPUはスタックのてっぺん以外の値にもアクセスすることが出来る
ストアもロードも出来る
ロードする時
code:s
srcのレジスタの値をアドレスとみなして値をロード
そのロードした値をdstに入れる
ex
raxに0x8000という数値が入っている場合を考える
code:s
raxに入っているアドレス値、ここでは0x8000のアドレスに入っている値をrdiに入れる
ストアする時
ロードの逆
code:s
dstに入っている数値をアドレスとみなして、そこにsrcの値を入れる
pushやpopはこの構文のエイリアスみたいなものである
code:s
// この2つは同じ意味
pop rax
add rsp, 8
code:s
// この2つは同じ意味
push rax
sub rsp, 8
ステップ13: ブロック
ステップ14: 関数の呼び出しに対応する
ステップ15: 関数の定義に対応する
バイナリレベルのインターフェイス
コンピュータにおける整数の表現
符号なし整数
符号あり整数
符号拡張
符号の反転
ポインタと文字列リテラル
ステップ16: 単項&と単項*
ステップ17: 暗黙の変数定義を廃止して、intというキーワードを導入する
ステップ18: ポインタ型を導入する
ポインタを表す型を定義する
ポインタが指している値に代入する
ステップ19: ポインタの加算と減算を実装する
ステップ20: sizeof演算子
ステップ21: 配列を実装する
配列型を定義する
配列からポインタへの暗黙の型変換を実装する
ステップ22: 配列の添字を実装する
ステップ23: グローバル変数を実装する
ステップ24: 文字型を実装する
ステップ25: 文字列リテラルを実装する
ステップ26: 入力をファイルから読む
ステップ27: 行コメントとブロックコメント
ステップ28: テストをCで書き直す
プログラムの実行イメージと初期化式
実行ファイルの構造
データセグメントの内容
初期化式の文法
グローバル変数の初期化式
ローカル変数の初期化式
ステップ29以降
Cの型の構文
型を表す図
型を表す記法
Cの型の読み方
ネストしていない型の読み方
ネストしている型の読み方
練習問題
おわりに
付録1:x86-64命令セット チートシート
整数レジスタの一覧
メモリアクセス
関数呼び出し
条件分岐
条件代入
整数・論理演算
付録2:Gitによるバージョン管理
Gitを使ったワークフロー
コミットするときの注意点
Gitの内部構造
参考資料
索引