[インデックス 1622] ファイルの概要
このコミットは、Goコンパイラの内部処理に関するもので、具体的にはsrc/cmd/gc/dcl.c
とsrc/cmd/gc/sysimport.c
の2つのファイルを変更しています。
src/cmd/gc/dcl.c
: Goコンパイラの宣言処理(declaration processing)を担当する部分です。ここでは、関数の初期化処理に関連するロジックが変更されています。src/cmd/gc/sysimport.c
:sys
パッケージとunsafe
パッケージのインポートに関する情報(コンパイラが内部的に使用するプロトタイプ宣言など)を定義しているファイルです。
コミット
do not generate Init proto for sys and unsafe.
R=ken
OCL=24455
CL=24455
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/aab26a52487861b53c89102f248d6cff0f88a583
元コミット内容
do not generate Init proto for sys and unsafe.
変更の背景
このコミットは、Goコンパイラ(gc
)のビルドプロセスにおける最適化と正確性の向上を目的としています。Go言語には、パッケージの初期化時に自動的に実行されるinit
関数という特別な関数があります。コンパイラは、これらのinit
関数のプロトタイプ(宣言)を生成し、パッケージのロード時に適切に呼び出されるように管理します。
しかし、sys
パッケージとunsafe
パッケージはGo言語のランタイムやコンパイラ自体が内部的に使用する特殊なパッケージであり、通常のGoプログラムから直接インポートして使用するものではありません(unsafe
パッケージは例外的にユーザーコードから使用されることがありますが、そのinit
関数の扱いは特殊です)。
Goコンパイラのビルド過程において、sys.go
やunsafe.go
といったファイルがコンパイルされる際、コンパイラはこれらのファイルが一時的に「PACKAGE」という名前のパッケージとして扱われることがあります。この段階で、通常のパッケージと同様にInit
関数のプロトタイプが生成されてしまうと、それは不要な処理であり、場合によってはコンパイラの内部的な整合性に問題を引き起こす可能性がありました。
このコミットは、sys
およびunsafe
パッケージに対して不要なInit
プロトタイプが生成されるのを防ぐことで、コンパイラのビルドプロセスをより効率的かつ堅牢にするための修正です。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびGoコンパイラに関する前提知識が必要です。
- Goコンパイラ (
gc
): Go言語の公式コンパイラであり、src/cmd/gc
ディレクトリにそのソースコードが存在します。Goのソースコードを機械語に変換する役割を担います。 init
関数: Go言語の各パッケージは、func init() { ... }
という形式のinit
関数を持つことができます。この関数は、パッケージがインポートされ、そのパッケージ内の変数がすべて初期化された後に、自動的に実行されます。これは、パッケージ固有の初期化ロジック(例: グローバル変数の設定、リソースの初期化など)を実行するために使用されます。sys
パッケージ: Go言語の内部パッケージであり、Goランタイムがシステムコールや低レベルな操作を行うために使用します。通常のGoプログラムから直接インポートして使用することは想定されていません。unsafe
パッケージ: Go言語の標準ライブラリの一部ですが、その名の通り「安全でない」操作(例: ポインタ演算、任意の型へのポインタ変換)を可能にするパッケージです。Goの型安全性をバイパスするため、注意して使用する必要があります。このパッケージも、Goランタイムや特定の低レベルなライブラリで利用されます。- プロトタイプ (Prototype): プログラミング言語の文脈では、関数や変数の「宣言」を指します。コンパイラは、コードを生成する前に、使用される関数や変数の型、引数、戻り値などの情報を把握しておく必要があります。これをプロトタイプ宣言と呼びます。
- コンパイラのビルドプロセス: Goコンパイラ自体もGo言語で書かれており、自己ホスト型(self-hosting)です。つまり、Goコンパイラをビルドするためには、既存のGoコンパイラが必要です。このビルドプロセス中には、コンパイラが自身の内部パッケージ(
sys
やunsafe
など)をコンパイルする特殊なフェーズが存在します。このフェーズでは、一時的なパッケージ名が使用されることがあります。 fninit
関数 (indcl.c
):src/cmd/gc/dcl.c
内のfninit
関数は、Goコンパイラがinit
関数に関連する処理を行うための内部関数です。具体的には、init
関数のプロトタイプを生成したり、その呼び出しをスケジュールしたりする役割を担っています。sysimport.c
: このファイルは、Goコンパイラがsys
やunsafe
といった特殊なパッケージの内部的な定義(関数シグネチャなど)を認識するために使用する文字列定数を含んでいます。これらは、Goのソースコードとしてではなく、コンパイラが直接解釈する形式で記述されています。
技術的詳細
このコミットの技術的な核心は、Goコンパイラの内部的なinit
関数プロトタイプ生成ロジックの修正にあります。
Goコンパイラは、ソースコードを解析する際に、各パッケージのinit
関数を識別し、そのプロトタイプ(内部的な表現)を生成します。このプロトタイプは、最終的な実行可能ファイルを生成する際に、init
関数が適切に呼び出されるようにするために必要です。
しかし、sys
パッケージとunsafe
パッケージは、Goの通常のパッケージとは異なる特殊な性質を持っています。これらはGoランタイムの非常に低レベルな部分を構成しており、そのinit
関数は通常のGoプログラムのinit
関数とは異なる方法で扱われるか、あるいは全くinit
関数を持たないことが期待されます。
問題は、コンパイラが自身のビルドプロセス中にsys.go
やunsafe.go
をコンパイルする際、一時的にこれらのファイルが「PACKAGE」という仮のパッケージ名で処理される点にありました。この「PACKAGE」という名前は、コンパイラが内部的に使用するプレースホルダーであり、実際のパッケージ名(sys
やunsafe
)が確定する前の段階で使われます。
src/cmd/gc/dcl.c
内のfninit
関数は、この「PACKAGE」という仮のパッケージ名を持つファイルに対しても、通常のinit
関数のプロトタイプ生成ロジックを適用しようとしていました。これは、sys
やunsafe
パッケージのinit
関数が、コンパイラが期待する形式で存在しないため、不要なプロトタイプが生成されたり、コンパイラの内部状態が不整合になったりする原因となっていました。
また、src/cmd/gc/sysimport.c
には、sys.Init·sys
やunsafe.Init·unsafe
といった、コンパイラが内部的に認識するInit
関数のプロトタイプ文字列がハードコードされていました。これらは、おそらく過去のコンパイラのバージョンで必要とされていたか、あるいは誤って含まれていたものです。これらのプロトタイプは、実際のsys
やunsafe
パッケージの動作とは一致せず、混乱を招く可能性がありました。
このコミットは、以下の2つの変更によってこの問題を解決しています。
dcl.c
における早期リターン:fninit
関数内で、現在のパッケージ名が「PACKAGE」である場合に、init
プロトタイプの生成処理をスキップするように変更しました。これにより、コンパイラのビルド中にsys.go
やunsafe.go
が一時的に「PACKAGE」として扱われる際に、不要なinit
プロトタイプが生成されるのを防ぎます。sysimport.c
からの不要なプロトタイプ削除:sysimport.c
から、sys.Init·sys
とunsafe.Init·unsafe
という文字列のプロトタイプ宣言を削除しました。これにより、コンパイラがこれらの特殊なパッケージに対して、存在しないInit
関数を誤って認識することを防ぎます。
これらの変更により、Goコンパイラはsys
およびunsafe
パッケージをより正確に、かつ効率的に処理できるようになり、コンパイラ自身のビルドの堅牢性が向上します。
コアとなるコードの変更箇所
src/cmd/gc/dcl.c
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -1050,6 +1050,11 @@ fninit(Node *n)
uint32 h;
Sym *s;
+ if(strcmp(package, "PACKAGE") == 0) {
+ // sys.go or unsafe.go during compiler build
+ return;
+ }
+
r = N;
// (1)
src/cmd/gc/sysimport.c
--- a/src/cmd/gc/sysimport.c
+++ b/src/cmd/gc/sysimport.c
@@ -59,12 +59,10 @@ char *sysimport =
"func sys.Goexit ()\\n"
"func sys.Exit (? int)\\n"
"func sys.Caller (n int) (pc uint64, file string, line int, ok bool)\\n"
-" \"func sys.Init·sys ()\\n\"
"\\n"
"$$\\n";
char *unsafeimport =
"package unsafe\\n"
"type unsafe.Pointer *any\\n"
-" \"func unsafe.Init·unsafe ()\\n\"
"\\n"
"$$\\n";
コアとなるコードの解説
src/cmd/gc/dcl.c
の変更
fninit
関数は、Goコンパイラがinit
関数を処理する際に呼び出される内部関数です。追加されたコードは以下の通りです。
if(strcmp(package, "PACKAGE") == 0) {
// sys.go or unsafe.go during compiler build
return;
}
strcmp(package, "PACKAGE") == 0
: これは、現在のコンパイル対象のパッケージ名が文字列「PACKAGE」と一致するかどうかをチェックしています。// sys.go or unsafe.go during compiler build
: このコメントが示すように、「PACKAGE」という名前は、Goコンパイラが自身のビルド中にsys.go
やunsafe.go
といった特殊なファイルをコンパイルする際に一時的に使用する内部的なプレースホルダーです。return;
: もしパッケージ名が「PACKAGE」であれば、fninit
関数の残りの処理(init
関数のプロトタイプ生成など)をスキップして、すぐにリターンします。
この変更により、sys
やunsafe
といった特殊なパッケージがコンパイラのビルド中に一時的な名前で処理される際に、不要なinit
関数のプロトタイプが生成されるのを防ぎます。これにより、コンパイラの内部的な整合性が保たれ、不必要な処理が削減されます。
src/cmd/gc/sysimport.c
の変更
このファイルは、Goコンパイラがsys
およびunsafe
パッケージの内部的な定義を認識するために使用する文字列定数を含んでいます。変更内容は、以下の2行の削除です。
"func sys.Init·sys ()\\n"
"func unsafe.Init·unsafe ()\\n"
これらの行は、それぞれsys
パッケージとunsafe
パッケージにInit·sys
およびInit·unsafe
という名前のinit
関数が存在するというプロトタイプ宣言をコンパイラに伝えていました。しかし、これらの関数は実際には存在しないか、あるいは通常のinit
関数とは異なる方法で扱われるべきものです。
これらの不要なプロトタイプ宣言を削除することで、コンパイラがsys
およびunsafe
パッケージのinit
関数に関して誤った仮定をすることを防ぎます。これにより、コンパイラの内部的な定義が実際のパッケージの動作と一致し、より正確なコンパイルが可能になります。
関連リンク
- Go言語の
init
関数に関する公式ドキュメント: https://go.dev/doc/effective_go#init - Go言語の
unsafe
パッケージに関する公式ドキュメント: https://pkg.go.dev/unsafe - Goコンパイラのソースコード(
src/cmd/gc
ディレクトリ): https://github.com/golang/go/tree/master/src/cmd/gc
参考にした情報源リンク
- Go言語の
init
関数に関する一般的な情報源(例: Go by Example, A Tour of Goなど) - Goコンパイラの内部構造に関する一般的な情報源(例: Goのコンパイラ設計に関するブログ記事や論文など)
strcmp
関数のC言語における動作に関する情報- Go言語の
sys
パッケージに関する情報(主にGoのソースコード内のコメントや関連する議論) - GitHubのGoリポジトリにおける関連するコミットやIssueの議論(もしあれば)
- Go言語のコンパイラがどのようにパッケージを処理するかに関する一般的な知識
- Go言語のビルドプロセスに関する情報