Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18456] ファイルの概要

このコミットは、Goコンパイラのバックエンド(cmd/5g, cmd/6g, cmd/8g)におけるAUNDEF(未定義命令)の振る舞いを修正するものです。具体的には、未定義命令が後続命令を持たないようにすることで、コンパイラの最適化器とGC(ガベージコレクション)のライブネス解析が誤った判断を下すのを防ぎます。

コミット

commit 85e4cb2f10eb1bbc147b6b549f48eb243c3517e3
Author: Daniel Morsing <daniel.morsing@gmail.com>
Date:   Tue Feb 11 20:25:40 2014 +0000

    cmd/6g, cmd/8g, cmd/5g: make the undefined instruction have no successors
    
    The UNDEF instruction was listed in the instruction data as having the next instruction in the stream as its successor. This confused the optimizer into adding a load where it wasn't needed, in turn confusing the liveness analysis pass for GC bitmaps into thinking that the variable was live.
    
    Fixes #7229.
    
    LGTM=iant, rsc
    R=golang-codereviews, bradfitz, iant, dave, rsc
    CC=golang-codereviews
    https://golang.org/cl/56910045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/85e4cb2f10eb1bbc147b6b549f48eb243c3517e3

元コミット内容

Goコンパイラのcmd/5g(ARM)、cmd/6g(x86-64)、cmd/8g(x86)において、未定義命令(AUNDEF)が命令データ内で次の命令を後続として持つようにリストされていた問題を修正します。この誤った設定により、最適化器が不要なロード命令を追加し、その結果、GCビットマップのライブネス解析が変数を誤って「ライブ(使用中)」と判断していました。このコミットは、AUNDEF命令が後続を持たないように変更することで、この問題を解決します。

変更の背景

この変更は、Goコンパイラの最適化器とガベージコレクション(GC)の正確性に関するバグ(Issue #7229)を修正するために行われました。Goのコンパイラは、生成されたコードの効率を最大化するために様々な最適化を行います。また、Goのガベージコレクタは、メモリを効率的に管理するために、どの変数がまだ使用されているか(ライブであるか)を正確に判断する必要があります。

問題は、AUNDEF命令(通常は到達不能なコードパスや、コンパイラが特定の命令のセマンティクスを完全に理解していない場合に挿入されるプレースホルダーのようなもの)の定義にありました。以前のコンパイラでは、このAUNDEF命令が、あたかも通常の命令のように「次の命令が後続である」と誤ってマークされていました。

この誤ったマーク付けが、以下の連鎖的な問題を引き起こしました。

  1. 最適化器の混乱: コンパイラの最適化器は、命令間の依存関係やフローを分析してコードを最適化します。AUNDEFが後続を持つと誤解されたため、最適化器は、実際には不要なメモリロード命令を挿入してしまうことがありました。これは、最適化器が変数のライフタイムを誤って評価した結果です。
  2. GCライブネス解析の誤り: GoのGCは、プログラム実行中にどの変数がまだ参照されているかを判断する「ライブネス解析」を行います。この解析は、変数が「ライブ」であると判断された場合、その変数が指すメモリを解放しないようにします。最適化器が不要なロードを追加したことで、GCのライブネス解析は、実際にはもう使用されていないはずの変数を「ライブ」であると誤って判断してしまいました。これにより、メモリリークや、GCの効率低下につながる可能性がありました。

このコミットは、AUNDEF命令の定義を修正し、それが後続命令を持たないことを明示することで、これらの問題を根本的に解決することを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGoコンパイラとランタイムに関する基本的な知識が必要です。

  1. Goコンパイラの構造:

    • Goコンパイラは、フロントエンド、ミドルエンド、バックエンドの3つの主要なフェーズに分かれています。
    • フロントエンド: ソースコードを解析し、抽象構文木(AST)を生成します。
    • ミドルエンド: ASTを中間表現(IR)に変換し、プラットフォーム非依存の最適化を行います。
    • バックエンド: IRを特定のアーキテクチャ(x86, ARMなど)の機械語に変換し、プラットフォーム依存の最適化を行います。このコミットで変更されているcmd/5g, cmd/6g, cmd/8gは、それぞれARM、x86-64、x86アーキテクチャ向けのバックエンドコンパイラです。
    • ProgInfoと命令データ: Goコンパイラのバックエンドでは、各命令(Prog)に関するメタデータがProgInfo構造体として定義されています。これには、命令のタイプ(疑似命令、読み込み、書き込みなど)や、命令が後続命令を持つかどうかといった情報が含まれます。
  2. 最適化器:

    • コンパイラの最適化器は、生成される機械語コードの実行速度やサイズを改善するために、様々な変換を行います。これには、不要な命令の削除、命令の並べ替え、レジスタ割り当ての最適化などが含まれます。
    • 最適化器は、命令間のデータフローや制御フローを分析し、変数のライフタイム(いつ生成され、いつ使用されなくなるか)を追跡します。
  3. ガベージコレクション(GC)とライブネス解析:

    • Goは自動メモリ管理(ガベージコレクション)を採用しています。プログラマは手動でメモリを解放する必要がありません。
    • GCは、プログラムがもうアクセスできないメモリ領域(「デッド」なオブジェクト)を特定し、それを解放して再利用可能にします。
    • ライブネス解析(Liveness Analysis): GCの重要なフェーズの一つで、特定の時点でどの変数が「ライブ」(将来的にプログラムによって読み取られる可能性がある)であるかを判断します。変数がライブであると判断された場合、その変数が指すメモリはGCによって解放されません。
    • GCビットマップ: GoのGCは、スタックやヒープ上のポインタを識別するためにビットマップを使用します。ライブネス解析の結果に基づいて、これらのビットマップが生成され、GCがポインタを正確に追跡できるようにします。
  4. AUNDEF命令:

    • AUNDEFは「Undefined Instruction(未定義命令)」の略です。これは、コンパイラが特定の状況で生成する特殊な命令で、通常は到達不能なコードパスや、コンパイラが特定の命令のセマンティクスを完全に理解していない場合に、安全策として挿入されます。
    • 本来、AUNDEF命令が実行されることは想定されておらず、もし実行されればプログラムはクラッシュするはずです。そのため、この命令は後続命令を持つべきではありません。

技術的詳細

このコミットの技術的詳細は、Goコンパイラのバックエンドにおける命令の定義方法と、それが最適化器およびGCのライブネス解析にどのように影響するかに関わっています。

Goコンパイラのバックエンド(cmd/5g, cmd/6g, cmd/8g)では、各命令タイプ(ALASTで定義される列挙型)に対して、その命令の特性を記述するProgInfo構造体の配列progtableが定義されています。このProgInfoには、命令が疑似命令であるか、メモリを読み込むか、書き込むか、そして最も重要な点として、その命令が後続命令を持つかどうかが含まれます。

以前のコードでは、AUNDEF命令のProgInfo{OK}と設定されていました。ここでOKは、命令が通常の制御フローを持ち、次の命令がその直接の後続であることを示します。これは、AUNDEF命令の本来の意図(実行されるべきではない、後続を持たない)に反していました。

この誤った設定が、以下の問題を引き起こしました。

  • 最適化器の誤動作: 最適化器は、命令の制御フローグラフ(CFG)を構築し、それに基づいて最適化を行います。AUNDEFOKとマークされていると、最適化器はAUNDEFの後に続く命令が存在し、その命令がAUNDEFの実行パスの一部であると誤解します。これにより、最適化器は、実際には到達しないコードパス上の変数に対して、不要なロード命令を挿入してしまうことがありました。例えば、ある変数がAUNDEFの後にライブであると誤って判断され、その変数をレジスタにロードする命令が追加される、といった状況です。
  • GCライブネス解析の誤り: GCのライブネス解析は、プログラムの制御フローとデータフローを分析して、どの変数がライブであるかを判断します。最適化器が不要なロード命令を追加すると、そのロード命令が参照する変数が、GCのライブネス解析によって「ライブ」であると誤って判断される可能性があります。これは、変数が実際にはもう使用されていないにもかかわらず、GCがその変数が指すメモリを解放しないことを意味し、結果としてメモリリークやGCの効率低下につながります。

このコミットでは、AUNDEFProgInfo{OK}から{Break}に変更しています。

  • Break: このフラグは、命令が制御フローを中断し、後続命令を持たないことを示します。これは、AUNDEF命令の本来のセマンティクスに合致します。Breakが設定された命令は、その後の命令に制御が移らないため、最適化器はAUNDEFの後に続くコードパスを考慮しなくなり、不要なロード命令の挿入を防ぎます。
  • GCライブネス解析の修正: 最適化器が不要なロード命令を挿入しなくなるため、GCのライブネス解析も変数を誤ってライブと判断することがなくなります。これにより、GCはより正確にメモリを管理できるようになります。

この変更は、Goコンパイラのバックエンドにおける命令のセマンティクスを正確に反映させることで、最適化器とGCのライブネス解析の正確性を向上させ、潜在的なバグやパフォーマンスの問題を解決します。

コアとなるコードの変更箇所

このコミットでは、以下の3つのファイルが変更されています。

  • src/cmd/5g/prog.c
  • src/cmd/6g/prog.c
  • src/cmd/8g/prog.c

それぞれのファイルで、progtable配列内のAUNDEFエントリの定義が変更されています。

--- a/src/cmd/5g/prog.c
+++ b/src/cmd/5g/prog.c
@@ -26,7 +26,7 @@ static ProgInfo progtable[ALAST] = {
 	[ATEXT]=\t{Pseudo},\n
 	[AFUNCDATA]=\t{Pseudo},\n
 	[APCDATA]=\t{Pseudo},\n
-	[AUNDEF]=\t{OK},\n
+	[AUNDEF]=\t{Break},\n
 	[AUSEFIELD]=\t{OK},\n
 	[ACHECKNIL]=\t{LeftRead},\n
 	[AFATVARDEF]=\t{Pseudo | RightWrite},\n

他のsrc/cmd/6g/prog.csrc/cmd/8g/prog.cも同様の変更です。

コアとなるコードの解説

変更の中心は、progtableという静的配列です。この配列は、Goコンパイラの各バックエンド(5g, 6g, 8g)において、Goの命令セット内の各命令(Aで始まる定数で表される)に関するメタデータを提供します。

progtableの各エントリはProgInfo構造体であり、命令の特性を記述するフラグの集合を含んでいます。

  • [AUNDEF]:これは、AUNDEF命令に対応するProgInfoエントリを指定しています。
  • {OK}:変更前の値です。OKフラグは、命令が通常の制御フローを持ち、その命令の次に続く命令がその直接の後続であることを示します。これは、ほとんどの通常の命令に適用されます。しかし、AUNDEF命令は未定義であり、実行されるべきではないため、この設定は誤りでした。
  • {Break}:変更後の値です。Breakフラグは、命令が制御フローを中断し、後続命令を持たないことを示します。これは、ジャンプ命令やリターン命令、そして今回のAUNDEFのように、その命令が実行された場合にプログラムの通常のシーケンシャルな実行が停止または変更される場合に用いられます。

この変更により、コンパイラの最適化器はAUNDEF命令の後に続くコードを、到達不能なコードとして扱うようになります。これにより、最適化器はAUNDEFの後に続く変数に対して不要なロード命令を挿入しなくなり、結果としてGCのライブネス解析が変数を誤ってライブと判断することもなくなります。

この修正は、Goコンパイラの命令セマンティクスを正確に反映させ、コンパイラの生成するコードの正確性と効率性を向上させるための重要な変更です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Goコンパイラのソースコード(特にsrc/cmd/5g/prog.c, src/cmd/6g/prog.c, src/cmd/8g/prog.c
  • コンパイラの最適化に関する一般的な情報源
  • ガベージコレクションとライブネス解析に関する一般的な情報源
  • Go言語のIssueトラッカーとコードレビューシステム