Sigrun
ツールチェインを構成するコンパイラ
独自言語で記述されたソースコードを入力として、x86_64アセンブリを出力する。
雑記
おそらくツールチェインの中で一番開発時間が長いのはこれ
対応している命令が少なくても言語機能の実装に困ることはあまりなかった
mov命令だけにして難読化するツールがあるぐらいなので、実行速度を考えなければそんなものなのかもしれない
regallocは手を抜いた
レジスタマシンは難しいが、スタックマシンにはしたくない
ということで簡易的なregallocを実装した
生存期間を考慮せず、一つのIRごとにレジスタの確保・開放をおこなっているだけ
加算が連続しているとすぐレジスタが枯渇する
1 + 2 + 3 + 4 + 5 + 6 + ...とか
最適化の併用でとりあえずなんとかなっている
virtual_indexとphysical_indexの意味がわかったので良しとしよう
IRは自分で設計した
多分初めて
9ccの影響を多大に受けているかもしれない
MIRくらい?
x86_64ではないアーキテクチャの機械語への変換とかしてみると不都合が出てきて面白そうなのでいつかやってみたい
機械語の生成は文字列で行っている
将来x86asmが成長したら、その内部表現を使うかもしれないし使わないかもしれない
現状困ってないので優先度は低い
機械語レベルでの最適化がしやすくなるかな、というのが意図としてある
現状だと高頻度で同じレジスタに対してのmovを生成してしまう
あと簡単のためにレジスタを介して演算を行っている
例えばmov rax, 1がmov r12, 1; mov rax, r12になる
これはメモリ間の値の代入がエラーになるのを防ぐため
mov [r12], [r13]を平気で生成しやがる
左辺値の概念を導入してからあまり考えなくなったが、初期の頃はよく問題になっていた
たぶん https://github.com/project-ela/sigrun/blob/9b333a7f47250f7d3f5c3ebfa0518c55f3f3b4c8/src/middleend/irgen.rs#L290 あたり
ポインタのderefのパースが大変だった
例えば以下のコード
code: go
var a: int = 1
var b: *int = &a
*b = 1
var b = &a * b = 1のように乗算の*としてパースされてしまう
自作言語では行末を指示するものがない
内部的にはそれぞれのトークンがスペースで区切られているに等しい
がばがばな対策をしていてまだ根本的には解決できていない
https://github.com/project-ela/sigrun/blob/9b333a7f47250f7d3f5c3ebfa0518c55f3f3b4c8/src/frontend/parser.rs#L417
こんな話はGitHubのレポジトリでTODOを検索すると山ほど出てくる気がする
ちなみに関数の返り値の型が正しいかの確認はされていない
手抜きである
symbol_passで型検査とシンボルの検査を行っている
ASTを更新して型情報を付加するか迷っていた
そうすると後でいろいろ便利になる
結局passでASTには一切変更を加えないことにした(&Programを受け取っている)