[インデックス 18515] ファイルの概要
このコミットは、Goコンパイラおよびリンカ内部で使用される特定の擬似命令の名称変更に関するものです。具体的には、AFATVARDEF
という名称がAVARDEF
へと変更されています。この変更は、コードの意図をより正確に反映させるためのものであり、機能的な変更は伴いません。
コミット
commit 801e40a0a4320827d52eca60bb42946cc5f81fc4
Author: Russ Cox <rsc@golang.org>
Date: Thu Feb 13 22:17:22 2014 -0500
cmd/gc: rename AFATVARDEF to AVARDEF
The "fat" referred to being used for multiword values only.
We're going to use it for non-fat values sometimes too.
No change other than the renaming.
TBR=iant
CC=golang-codereviews
https://golang.org/cl/63650043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/801e40a0a4320827d52eca60bb42946cc5f81fc4
元コミット内容
cmd/gc: rename AFATVARDEF to AVARDEF
"fat"
という言葉は、複数ワードの値にのみ使用されることを指していました。
しかし、今後は「fat」ではない値にも使用されることがあります。
名称変更以外の変更はありません。
変更の背景
このコミットの背景には、Goコンパイラ(cmd/gc
)および関連するアセンブラ/リンカ(cmd/5g
, cmd/6g
, cmd/8g
, cmd/5l
, cmd/6l
, cmd/8l
, src/liblink
)の内部における、変数定義を扱う擬似命令の命名規則の改善があります。
元々AFATVARDEF
という名称が使われていましたが、これは「fat」(複数ワード、つまりポインタや構造体などのサイズが大きい値)な変数にのみ適用されるという誤解を招く可能性がありました。しかし、Goコンパイラの進化に伴い、この擬似命令が「fat」ではない(単一ワードの)値にも適用されるケースが出てくることが予見されました。
このような状況でAFATVARDEF
という名称を維持することは、コードの可読性を損ない、将来的な誤解やバグの原因となる可能性がありました。そのため、より汎用的なAVARDEF
という名称に変更することで、この擬似命令が扱う変数の種類に関する制約がないことを明確にし、コードベースの一貫性と正確性を向上させる目的でこの変更が行われました。機能的な変更は一切なく、純粋にセマンティックな改善が目的です。
前提知識の解説
このコミットを理解するためには、以下のGoコンパイラおよびリンカに関する基本的な概念を理解しておく必要があります。
-
Goコンパイラ (
cmd/gc
):- Go言語のソースコードを機械語に変換する主要なコンパイラです。
- コンパイルの過程で、抽象構文木(AST)を生成し、型チェック、最適化、コード生成などを行います。
- コード生成の段階で、アセンブラに渡すための命令列(擬似命令を含む)を生成します。
-
Goアセンブラ (
cmd/5g
,cmd/6g
,cmd/8g
):- Goコンパイラが生成した中間コード(Goアセンブリ)を、特定のアーキテクチャ(例:
5g
はARM、6g
はx86-64、8g
はx86-32)のオブジェクトファイルに変換します。 - これらのアセンブラは、Go言語のランタイムや標準ライブラリの低レベルな部分を実装するためにも使用されます。
- Goコンパイラが生成した中間コード(Goアセンブリ)を、特定のアーキテクチャ(例:
-
Goリンカ (
cmd/5l
,cmd/6l
,cmd/8l
):- アセンブラによって生成されたオブジェクトファイルや、他のライブラリを結合して実行可能ファイルを生成します。
- シンボル解決、再配置、セクションの結合などを行います。
-
擬似命令 (Pseudo-instructions):
- 実際のマシン語命令には直接対応しないが、コンパイラやアセンブラの内部処理で特定の意味を持つ命令です。
- これらは、コンパイル時やリンク時に特別な処理をトリガーするために使用されます。
AFATVARDEF
やAVARDEF
は、このような擬似命令の一種であり、変数の定義に関連するメタデータや処理を示すために使われます。
-
Node
構造体:- Goコンパイラの内部で、抽象構文木(AST)の各要素を表すために使用されるデータ構造です。
- 変数、関数、式など、プログラムのあらゆる構成要素が
Node
として表現されます。
-
Prog
構造体:- Goコンパイラが生成する中間コード(アセンブリ命令)を表すデータ構造です。
- 各
Prog
インスタンスは、単一のアセンブリ命令または擬似命令を表し、オペランドやリンク情報などを含みます。
-
isfat
関数:- Goコンパイラ内部で使用されるヘルパー関数で、与えられた型が「fat」(複数ワード)であるかどうかを判定します。
- 「fat」な型とは、例えば構造体や配列、インターフェース、スライスなど、単一のレジスタやワードに収まらないサイズのデータを指します。
これらの概念を理解することで、AFATVARDEF
からAVARDEF
への名称変更が、単なる文字列の置換ではなく、コンパイラ内部のセマンティクスをより正確に表現するための重要な改善であることがわかります。
技術的詳細
このコミットは、Goコンパイラおよびリンカの内部で使用される擬似命令AFATVARDEF
をAVARDEF
にリネームするものです。この変更は、Goコンパイラのコード生成フェーズと、それに続くアセンブラおよびリンカの処理に影響を与えます。
Goコンパイラは、ソースコードを解析し、中間表現(IR)を生成します。このIRは、最終的にターゲットアーキテクチャの機械語に変換されるアセンブリ命令のシーケンスに似たProg
構造体のリストとして表現されます。AFATVARDEF
(そして新しいAVARDEF
)は、このProg
構造体で使用される擬似命令の一つです。
元々、AFATVARDEF
は「fat」(複数ワード)な変数の定義を示すために導入されました。これは、Goの型システムにおいて、ポインタ、スライス、インターフェース、構造体など、単一のCPUレジスタやメモリワードに収まらないサイズのデータ型を効率的に扱うための内部的なメカニズムに関連しています。これらの「fat」な値は、コンパイラが特別な方法でメモリを割り当てたり、初期化したり、ガベージコレクションの対象として追跡したりする必要がありました。AFATVARDEF
は、このような変数の定義ポイントをマークし、コンパイラやリンカが適切な処理を行うためのヒントとして機能していました。
しかし、Goコンパイラの開発が進むにつれて、この擬似命令が「fat」ではない(単一ワードの)値に対しても、特定の最適化やコード生成のシナリオで利用されるケースが出てくることが予見されました。例えば、スタック上の変数の初期化や、特定のスコープでの変数の生存期間管理など、サイズに関わらず変数の定義をマークする必要がある場面です。
AFATVARDEF
という名称は、「fat」な値に限定されるという誤解を招く可能性があり、将来的なコードの拡張性や保守性を阻害する恐れがありました。そこで、Russ Cox氏はこの擬似命令の名称をAVARDEF
(A-Variable-Definition)に変更することを決定しました。これにより、この擬似命令が「変数の定義」というより一般的な概念を表現するようになり、その用途が「fat」な値に限定されないことを明確にしました。
この変更は、主に以下のファイル群に影響を与えています。
src/cmd/*/ggen.c
: Goコンパイラのジェネレータ部分で、コード生成ロジックが含まれます。gfatvardef
関数の呼び出しがgvardef
に変更されています。src/cmd/*/peep.c
: Goコンパイラのピーフォール最適化部分です。AFATVARDEF
がAVARDEF
に置き換えられています。src/cmd/*/prog.c
:Prog
構造体に関連する情報(命令の種類やプロパティ)を定義する部分です。AFATVARDEF
がAVARDEF
に置き換えられています。src/cmd/*/?.out.h
: リンカが使用する命令コードの定義ファイルです。AFATVARDEF
がAVARDEF
に置き換えられています。src/cmd/gc/gen.c
: Goコンパイラの汎用コード生成部分です。gfatvardef
関数の呼び出しがgvardef
に変更されています。src/cmd/gc/go.h
: Goコンパイラのグローバルヘッダファイルで、gfatvardef
関数の宣言がgvardef
に変更されています。src/cmd/gc/pgen.c
: Goコンパイラの擬似命令生成部分です。gfatvardef
関数自体がgvardef
にリネームされ、その内部ロジック(特にfatal("gvardef: node is not fat")
というエラーメッセージ)も更新されています。また、removefatvardef
関数もremovevardef
にリネームされています。src/cmd/gc/plive.c
: ライブネス解析(変数がいつ使用され、いつ不要になるかを追跡する)に関連する部分です。prog->as == AFATVARDEF
の比較がprog->as == AVARDEF
に変更されています。src/liblink/asm?.c
: リンカのバックエンド部分で、アセンブリ命令の処理に関連します。AFATVARDEF
がAVARDEF
に置き換えられています。
このリネームは、コンパイラとリンカの複数のサブシステムにわたる広範な変更ですが、その性質上、機能的な動作には一切影響を与えません。これは、コードベースのセマンティックな正確性を高め、将来のメンテナンスと拡張を容易にするための「クリーンアップ」作業の一環と見なすことができます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、AFATVARDEF
という文字列をAVARDEF
に置き換えることです。これは、Goコンパイラおよびリンカの複数のファイルにわたって行われています。
主な変更箇所は以下の通りです。
-
擬似命令の定義:
src/cmd/5l/5.out.h
,src/cmd/6l/6.out.h
,src/cmd/8l/8.out.h
などのリンカのヘッダファイルで、擬似命令の列挙型定義が変更されています。- AFATVARDEF, + AVARDEF,
-
擬似命令の生成関数:
src/cmd/gc/go.h
で関数の宣言が変更されています。-void gfatvardef(Node*); +void gvardef(Node*);
src/cmd/gc/pgen.c
で関数定義自体が変更されています。-gfatvardef(Node *n) +gvardef(Node *n) { if(n == N || !isfat(n->type)) - fatal("gfatvardef: node is not fat"); + fatal("gvardef: node is not fat"); switch(n->class) { case PAUTO: case PPARAM: case PPARAMOUT: - gins(AFATVARDEF, N, n); + gins(AVARDEF, N, n); } }
-
擬似命令の参照箇所:
src/cmd/*/ggen.c
(例:src/cmd/5g/ggen.c
) で、gfatvardef
の呼び出しがgvardef
に変更されています。- gfatvardef(nl); + gvardef(nl);
src/cmd/*/peep.c
(例:src/cmd/5g/peep.c
) で、AFATVARDEF
の参照がAVARDEF
に変更されています。- case AFATVARDEF: + case AVARDEF:
src/cmd/*/prog.c
(例:src/cmd/5g/prog.c
) で、progtable
内の擬似命令参照が変更されています。- [AFATVARDEF]= {Pseudo | RightWrite}, + [AVARDEF]= {Pseudo | RightWrite},
src/cmd/gc/gen.c
で、gfatvardef
の呼び出しがgvardef
に変更されています。- gfatvardef(res); + gvardef(res);
src/cmd/gc/plive.c
で、ライブネス解析における擬似命令のチェックが変更されています。- if(from->node != nil && (!isfat(from->node->type) || prog->as == AFATVARDEF)) + if(from->node != nil && (!isfat(from->node->type) || prog->as == AVARDEF))
src/liblink/asm?.c
(例:src/liblink/asm6.c
) で、アセンブラの命令テーブル内の参照が変更されています。- { AFATVARDEF }, + { AVARDEF },
-
関連するヘルパー関数のリネーム:
src/cmd/gc/pgen.c
で、removefatvardef
関数がremovevardef
にリネームされています。-removefatvardef(Prog *firstp) +removevardef(Prog *firstp) { Prog *p; for(p = firstp; p != P; p = p->link) { - while(p->link != P && p->link->as == AFATVARDEF) + while(p->link != P && p->link->as == AVARDEF) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.u.branch != P && p->to.u.branch->as == AFATVARDEF) + while(p->to.u.branch != P && p->to.u.branch->as == AVARDEF) p->to.u.branch = p->to.u.branch->link; } }
これらの変更は、Goコンパイラとリンカの複数のサブシステムにわたる広範なリネーム作業であり、AFATVARDEF
という古い名称の痕跡を完全に排除することを目的としています。
コアとなるコードの解説
このコミットの核心は、Goコンパイラとリンカの内部で変数の定義をマークするために使用される擬似命令の名称をAFATVARDEF
からAVARDEF
に変更することです。この変更は、機能的な動作には影響を与えず、コードのセマンティックな正確性と将来の拡張性を向上させることを目的としています。
具体的に、この変更が影響する主要なコードパスと概念は以下の通りです。
-
gvardef
(旧gfatvardef
) 関数:- この関数は、Goコンパイラのコード生成フェーズで呼び出され、特定の
Node
(抽象構文木のノード、ここでは変数)に対してAVARDEF
擬似命令を生成します。 src/cmd/gc/pgen.c
にその実装があります。- 変更前は、この関数は
isfat(n->type)
(変数の型が「fat」であるか)のチェックを行い、!isfat(n->type)
であればfatal("gfatvardef: node is not fat")
というエラーを発生させていました。これは、AFATVARDEF
が「fat」な値にのみ使用されるという当初の意図を反映していました。 - 名称変更後もこの
isfat
チェックは残っていますが、コミットメッセージにあるように、将来的には「fat」ではない値にもこの擬似命令が使用される可能性があるため、このエラーメッセージはfatal("gvardef: node is not fat")
と変更され、より一般的なAVARDEF
の文脈に合うように調整されています。これは、この擬似命令の適用範囲が拡大する可能性を示唆しています。 gins(AVARDEF, N, n)
という呼び出しは、AVARDEF
という擬似命令を、指定された変数n
をオペランドとして、現在の命令ストリームに挿入することを意味します。
- この関数は、Goコンパイラのコード生成フェーズで呼び出され、特定の
-
removevardef
(旧removefatvardef
) 関数:src/cmd/gc/pgen.c
に実装されており、コンパイルの最終段階で、不要になったAVARDEF
擬似命令を命令ストリームから削除する役割を担います。AVARDEF
のような擬似命令は、コード生成の過程で一時的なマーカーとして使用されることがありますが、最終的な機械語には変換されないため、リンカに渡す前に削除する必要があります。- この関数は、命令リストを走査し、
p->link->as == AVARDEF
またはp->to.u.branch->as == AVARDEF
という条件でAVARDEF
命令を見つけてスキップすることで、命令ストリームから効果的に削除します。
-
ProgInfo progtable
:src/cmd/*/prog.c
に定義されているprogtable
は、各アセンブリ命令(および擬似命令)のプロパティ(例:Pseudo
,RightWrite
など)を定義するテーブルです。[AVARDEF]={Pseudo | RightWrite}
というエントリは、AVARDEF
が擬似命令であり(Pseudo
フラグ)、その右オペランドが書き込み操作であることを示しています(RightWrite
フラグ)。これは、変数の定義がメモリへの書き込み操作と見なされるためです。
-
ライブネス解析 (
src/cmd/gc/plive.c
):- ライブネス解析は、変数がプログラムのどの時点で「生きている」(つまり、将来使用される可能性がある)かを判断するコンパイラの最適化フェーズです。
plive.c
内のコードでは、prog->as == AFATVARDEF
という条件で、特定の擬似命令が変数のライブネスにどのように影響するかを判断していました。この条件がprog->as == AVARDEF
に変更されたことで、ライブネス解析が新しい名称の擬似命令を正しく認識し、変数の生存期間を正確に追跡できるようになります。
-
リンカの命令定義 (
src/cmd/*/?.out.h
,src/liblink/asm?.c
):- リンカは、コンパイラが生成したオブジェクトファイルを処理する際に、これらの擬似命令の定義を認識する必要があります。
- ヘッダファイル(例:
5.out.h
)でenum as
内の名称が変更され、リンカのバックエンドコード(例:asm6.c
)で命令テーブル内の参照が更新されたことで、リンカがAVARDEF
を正しく解釈できるようになります。
このコミットは、Goコンパイラの内部構造における命名の一貫性を高め、将来的な機能拡張や最適化のための基盤を強化するものです。特に、AVARDEF
が「fat」ではない値にも適用されるようになるという示唆は、コンパイラが変数の定義をより柔軟に、かつ統一的に扱うようになる可能性を示しています。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Goコンパイラの内部構造に関するドキュメント(Goのソースコード内や関連する論文を参照)
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/gc
,src/cmd/5g
,src/cmd/6g
,src/cmd/8g
,src/cmd/5l
,src/cmd/6l
,src/cmd/8l
,src/liblink
ディレクトリ) - Goコンパイラの設計に関する論文やブログ記事 (例: Russ Cox氏のブログ、Goの公式ブログ)
- GoのIssueトラッカーやコードレビューシステム (Go CL:
https://golang.org/cl/63650043
) - Goの内部構造に関するコミュニティの議論やドキュメント