記事の概要
最近書いていた自作Cコンパイラ「ycc」がセルフホストを達成しました。
これは後述するcompilerbookを読んで作ったものです。
まだまだ実装は足りていませんが、そろそろアウトプットしないとキツくなってきたので諸々をブログにまとめます。
続きを読むコンピュータシステムの理論と実装(通称、nand2tetris)*1という本がありますが、ひとまず完走しました。 1年前くらいに買った本ですが、かなり放置していました。1行でも本を読んだりコードを書いたりしたのは2週間強くらいだと思います。
他のブログでもよく取り上げられているので本の詳細は省きます*2が、満足したのでその紹介と感想を簡単に書くことにします。
コンピュータシステムの理論と実装(nand2tetris)、とりあえず終わった〜。
— ゆいき (@yuikijp) 2022年2月17日
図1: 自作標準ライブラリとともにRust製自作コンパイラでコンパイルされ動作している「pong」 pic.twitter.com/NmmQhVcGpg
NANDゲートを組み合わせるところから始め、最終的に高級言語で書かれたPong*3というゲームを動作させることを目的としています。
本書で扱うシステムはHackと呼ばれる16bitアーキテクチャのコンピュータです。16KBのRAMと512x256のスクリーンやキーボード*4をサポートしています。
なお、本書では最適化に関しては特別考慮しておらず、他の場所で学ぶことが想定されています。
全体的に各種シミュレータやテストが提供されているので、それをパスすることを目指して作っていけます。テストが用意されていることでかなり実装しやすく感じました。 テストプログラムが各ソフトウェアの完成度に合わせて用意されているので、一度に全部作る必要はなく段階的に実装することが可能です。 ちなみに各ソフトウェアは自由な言語で記述できるので、私はRustを使いました。
パーサを実装しやすいようにJack言語はシンタックスが工夫されています。この頃たまたま「実践Rust入門」を読んでいて、パーサの実装に関して記載があったので参考にした記憶があります。
また途中でパースしたものをXMLで吐き出しそれをテストする流れになっていましたが、少し面倒だったので私はメモリ上でASTを作りました。
各所で実装するプログラムはAPIが提示されているのですが、個人的には基本的には従うが完全には従わなくてもよいと思いました。 実装に使う言語によってはよりよいインターフェースがあるはずです。
nand2tetrisのアセンブリパーサのAPI、ちょっと実装しづらい…
— ゆいき (@yuikijp) 2021年4月16日
実装したコンパイラは、単純化のために例えばほとんどの演算子の優先順位が不定です。そのため、私は標準ライブラリの実装でコードをかなり書き間違えてしまい大変でした。 独自拡張として、慣れ親しんだ優先順位で解釈するコンパイラを作るのもありかもです。
括弧以外の演算子の優先順位が不定な言語でプログラム書くのしんどすぎるな
— ゆいき (@yuikijp) 2022年2月16日
書籍としては完了したのですが、Pongを自前のVMやアセンブラを使って動作させることはまだできていません(速度の観点で)。最適化に関しては今後やる気が出たらやろうと思います。
要所で大学で受講した講義のことを思い出しました。
大学のおさらい(一部)としてもなかなか良かったと思います。
概念レベルでは知っていることも多かったのですが、ある程度動くものを一気通貫して作るという点では実際やってよかったと思いました。
少し前の話題ですが、取り上げている記事が見当たらなかったためまとめます。
Androidプロジェクトにおいて3つのモジュールが以下のような依存関係にあったとします。
app -> intermediate -> db
appモジュールはApplicationクラスを持ち、モジュール間の依存関係はimplementationで記述されているとします。
このとき、マルチモジュールの利点の1つとして、
appモジュールがdbモジュールに直接依存していないことによって、dbモジュール内のコードを直接参照することを防ぐことができます。 … (1)
Dagger Hiltではルートのappモジュールにおいて全てのインスタンスを生成するためにそれらに直接参照できる必要があります。
つまりDagger Hiltを利用しようとすると、
appモジュールがdbモジュールに直接依存するか、intermediateモジュール内でdbモジュールへの依存関係をapiで定義する必要があります。 … (2)
これだと先ほどのマルチモジュールの利点 (1) が失われてしまいます。
この問題のワークアラウンドとして、Dagger Hilt 2.31.x-alpha以降からappモジュールの build.gradle
ファイルに以下のような記述をすれば (2) の対応を行わずとも (1) の利点を保つことができるようになります。
hilt { enableExperimentalClasspathAggregation = true }
ただし、まだExperimentalです。
詳しくはこちら dagger.dev
ざっとしかコードを読んでいないのですが、遷移的依存関係をコンパイル時に利用できるように CompileOnly
コンフィグを利用して依存関係を処理しているようです。
このような処理を行っているので、ビルド時処理が増えてしまいます。
これに関しては、以下のように述べられているので今後改善していくと思われます。
This solution is inefficient and will cause build performance impact, but it is a starting point that can be further optimized by using a smarter transform that can extract the necessary classes required by Dagger and Hilt.
この問題はHiltに限った話でなく、Daggerでも発生します。
幸いなことにDagger 2.29で追加された validateTransitiveComponentDependencies
オプションを利用すると解消されるようです。
これに関して解説しているページを見つけたので添付しておきます。