世界を動かす技術を、日本語で。

Zigのインクリメンタルコンパイルの内部構造

2026年7月29日原文(mlugg.co.uk)

概要

  • Zigコンパイラの インクリメンタルコンパイル 導入プロジェクトの解説
  • ソースコードの変更箇所のみ再コンパイルし、 高速な再ビルド を実現
  • パイプライン各段階での 依存関係管理 とキャッシュ戦略の詳細
  • 実際のアプリケーションでの 利用例 とデモ動画の紹介
  • 設計上の工夫や他言語との比較、 今後の展望 についても言及

Zigコンパイラにおけるインクリメンタルコンパイルの実装

  • Zigコアチームの一員として、 インクリメンタルコンパイル 機能の実装に携わった経験

  • この機能により、 変更された関数や宣言のみを再コンパイル し、バイナリへ直接パッチ適用

  • 結果として、再ビルドが 極めて高速 に完了する仕組み

    • 例:Fizzy(ピクセルエディタ)での初回ビルドは約5秒、以降の変更は 50〜70ms で完了
    • デモ動画では、 Zig master branch へのアップグレードが必要なことも解説
    • Zig 0.16.0では一部リンク機能が未実装のため、 安定版での利用は0.17.0以降 を推奨

ソースファイル処理パイプライン

  • Zigコンパイラのパイプラインは 複数の段階 に分かれている

  • 最初の段階は ソースファイル単位 での処理

    • ディスクからファイルを読み込み
    • 抽象構文木(AST)へパース
    • “AstGen”パスでASTを ZIR(Zig Intermediate Representation) へ変換
  • ZIRは 非型付きSSA形式IR であり、ファイル全体を別形式へ変換する役割

  • AstGen実行時には@import("foo.zig")などの インポート情報 も取得

  • このプロセスは 純粋関数的 で外部状態に依存しない

    • 各ファイルごとの処理は 並列化が容易
    • ZIRはディスクへの書き込み・読み込みが 高速(writev/readv)
    • 変更検知とキャッシュによる インクリメンタルビルド が容易に実現
  • Zigではこの最適化が 長年デフォルトで有効化 されており、ほとんどの場合この段階は 瞬時に完了

  • 進捗出力の"AST Lowering"がこの段階の実行を示す

セマンティック解析と依存関係管理

  • 次の重要な段階は セマンティック解析 (型チェック・comptime評価)

  • セマンティック解析の役割

    • 生成したZIRを「解釈」し、 型エラーなどのコンパイルエラー を検出
    • 実行時関数のための 中間表現生成
  • Zigでは "container-level declaration"(関数・グローバル定数・グローバル変数) 単位で解析

  • コンパイルを 独立した小さな単位(analysis unit) に分割し、依存関係を グラフ構造 で管理

    • 例:struct/unionのレイアウト、container-level宣言の型、const宣言の値、関数本体
    • 各ユニット間の依存関係を明示的に記録
  • 依存関係グラフの活用

    • 変更が発生した際、 どのユニットを再解析すべきか を即座に特定
    • ソースコードのハッシュ値を利用し、 変更検知と再解析のトリガー に活用
  • インライン関数呼び出し等、 より複雑な依存関係 も正しく追跡

実例:依存関係グラフの動作

  • サンプルコードとその 依存関係グラフ を図示
  • lucky_numberの値変更時、 該当する依存ユニットのみが再解析対象 となる
  • ソースコードの一部変更が、 最小限の再ビルド範囲 に限定される仕組み

設計上の工夫と他言語との比較

  • セマンティック解析の インクリメンタル対応 は難易度が高い
  • Zigでは言語設計自体を インクリメンタルコンパイル容易化 のために調整
  • 依存関係の分割・明示化により、 高速なビルド体験 を実現
  • 多くの現代的言語でも類似の手法は理論上可能だが、 設計上の工夫が重要

まとめと今後の展望

  • Zigのインクリメンタルコンパイルは、 実用レベルで日常的に活用可能
  • 設計・実装の工夫により、 大規模プロジェクトでも高速な再ビルド が実現
  • 今後も さらなる最適化や機能拡張 が期待される
  • 既にZigを利用中のユーザーは、 進捗出力やデモ動画 でその効果を体感可能

Hackerたちの意見

リリースビルドでも使えるの?それともデバッグビルドだけ?

今のところllvm/releaseでは動かないと思うけど、いつかはできるかもね。llvmでインクリメンタルリンカーを動かすのは結構難しいし。zigチームが何か計画を持ってるかは分からないけど。

うちの自己ホスト型コード生成バックエンド(主にx86_64用)でしか動かないんだ。今のところ最適化パスはないけど、将来的には計画してるよ。けど、それは長期的な目標ね。それに、関数間で情報を伝播する最適化(最も明白で重要なのはインライン化)は、インクリメンタルコンパイルとはほぼ互換性がない(これについては投稿で触れたと思う)。だから、これが動くようになったら、たぶん一部の最適化に限られると思う。要するに、今はデバッグビルドだけで、いつか自分たちの最適化パスができればリリースビルドにも拡張できるかもしれないけど、いくつかの最適化は適用できないままだと思う。

zigコンパイラはCもコンパイルできるから、Cでも使えるの?

そうじゃないよ!プロジェクトがCとZigの混合なら、Zigを編集するのはいいけど、Cを編集するのはダメだよ。ZigコンパイラはZigのAIRをキャッシュするけど、Cの情報はキャッシュしないからね。それに、CのコンパイルはLLVMでしかできないし、C用の自己ホストバックエンドを書くのはめんどくさいよ。

業界がコンパイル速度をこんなに長い間真剣に考えてこなかったのが残念だよ。Zigがすごく価値のある、高いインパクトのある仕事をしてるのを見ると嬉しいね。

Golangの存在理由は、ほぼコンパイル速度だね。

そうそう、Modula-2やObject Pascalのコンパイラの実行速度に驚く人がいるよね。

そういえば、昔はJavaがC++開発者からすごく人気だったんだ。C++よりもずっと早いコンパイルが重要だったからね。15年以上前でもそれは重要だった。みんな、コーディング中の反復時間が早くなることを褒めてたよ。

Zigのツールチェーンの進化はいつも感心させられる。まだソフトウェアを書くつもりはないけど、メモリ安全性が基本だと思ってるから、これらの進展は本当に素晴らしい。インクリメンタル作業の前は、ツールチェーンやクロスコンパイラの作業があったし、ツールチェーンの部分はずっと素晴らしかった。次に何を思いつくのか、すごく楽しみだよ! > 「セマンティック解析は、コンパイラの中でインクリメンタルに扱うのが最も難しい部分です。」 これは驚くべきことではないけど、言語設計がすごく重要になってくるところだね。ほとんどの現代の言語が私たちのやり方でインクリメンタルコンパイルをサポートできる自信はあるけど、特定の設計の決定がそれを難しくすることもある。Zigは、速いインクリメンタルコンパイルをサポートしやすくするために、年々デザインが調整されてきた(時には物議を醸しながら)。これはRustでもやっておけばよかったなと思う。すべてを一度にやるのは不可能だし、すでにやるべきことが山ほどあったからね。これも「1.0をいつ出すか」というトレードオフの一部だし、私たちの言語の目標にとっては2015年が適切なタイミングだったけど、もう少し時間があれば、コンパイル時間をもっと速くできたかもしれない。ソフトウェアエンジニアリングは難しいね。

Rustの問題は、安全性を得るためにコンパイルが遅くなるっていうトレードオフがあることだよね。Zigは同じ安全性の保証がないから、安全なコーディングパターンを使うのは開発者次第。だから、安全性のためのトレードオフは、規律や経験になるんだ。

10年前、Rustの「インクリメンタル再コンパイル」に関する問題にコメントしたことがあるんだけど、Rustが少なくともHaskell GHCのインクリメンタリティモデルを採用できるかもしれないって提案されてたんだ。これは現在ファイルレベルでの話ね。https://github.com/rust-lang/rust/issues/2369#issuecomment-1... これだけでもかなり助かると思う。インクリメンタル再コンパイルに興味がある人は、GHCが何をしているかを読むことをおすすめするよ。達成するための努力は比較的少ないからね。もちろん、もっと良くしたいという欲求は常にあるけど、GHCは現在、他のファイルを処理する前にファイルをパース+型チェック+コード生成しなきゃいけない。コード生成は遅いから、現在は「段階」に分けてコンパイルする需要があるんだ。次のファイルは、そのインポートが型チェックされた後に型チェックできるようにね。関数レベルで再コンパイルを避けられたらいいなとも思ってる。マクロシステムは、インクリメンタル再コンパイルを壊す重要な言語機能なんだ。理論的には、Haskellはそれにうまく対応できるようになってる。マクロシステム(TemplateHaskell)は完全にASTベースで、理論的には「完全に純粋」なマクロと副作用のあるマクロを区別できるはずなんだ(例えば、現在のgitコミットを文字列リテラルとして挿入するような)。でも、再コンパイル回避システムは現在、その違いを活用していないんだよね。

Hacker Newsで議論の続きを見る