[インデックス 11509] ファイルの概要
このコミットは、Go言語のARMアーキテクチャ向けリンカである5l
のpatch()
関数における最適化に関するものです。具体的には、シンボル解決の一般的なケースにおいて、不要なシンボル値の検索をスキップすることでパフォーマンスを向上させています。
コミット
commit 2d64bab1ded3c17d1e73ee1dcacf1aafae223317
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Jan 31 10:59:34 2012 -0500
5l: optimize the common case in patch()
If p->to.sym->text is non-nil, then no need to search for sym->value.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5601046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2d66bab1ded3c17d1e73ee1dcacf1aafae223317
元コミット内容
5l: optimize the common case in patch()
If p->to.sym->text is non-nil, then no need to search for sym->value.
このコミットメッセージは、5l
リンカのpatch()
関数における一般的なケースの最適化を目的としていることを示しています。具体的には、シンボル(p->to.sym
)のtext
フィールドがnil
でない場合、つまりシンボルの実体(コードやデータ)が既に利用可能である場合、そのシンボルのvalue
(アドレスやオフセット)を改めて検索する必要がない、という最適化です。
変更の背景
Go言語のコンパイラとリンカは、Goプログラムを効率的に実行可能なバイナリに変換するために重要な役割を担っています。リンカの主な役割の一つは、コンパイルされたオブジェクトファイルやライブラリを結合し、シンボル参照を解決して、最終的な実行可能ファイルを生成することです。このプロセスには、関数呼び出しやデータ参照のアドレスを正確に解決し、必要に応じてコードを修正(パッチ適用)する作業が含まれます。
5l
はGoのARMアーキテクチャ向けリンカであり、組み込みシステムやモバイルデバイスなど、リソースが限られた環境でのパフォーマンスが特に重要です。リンカの処理速度は、開発サイクルやビルド時間に直接影響するため、可能な限り効率的であることが求められます。
このコミットの背景には、patch()
関数がシンボル解決の過程で頻繁に呼び出されるため、その内部のわずかな非効率性が全体のリンク時間に大きな影響を与える可能性があるという認識があったと考えられます。特に、シンボルのtext
フィールドが既に設定されている(つまり、シンボルの実体が既にメモリ上にロードされているか、その場所が確定している)場合、value
を再検索するような冗長な処理は避けるべきです。このような冗長な検索を排除することで、リンカのパフォーマンスを向上させ、ビルド時間を短縮することが目的とされています。
前提知識の解説
リンカの役割
リンカは、コンパイラによって生成された複数のオブジェクトファイル(.o
ファイルなど)やライブラリを結合し、単一の実行可能ファイルや共有ライブラリを生成するプログラムです。主な機能は以下の通りです。
- シンボル解決 (Symbol Resolution): プログラム内の関数や変数などのシンボル参照を、それらが定義されている実際のアドレスに解決します。例えば、
main
関数がfmt.Println
を呼び出す場合、リンカはfmt.Println
がメモリ上のどこにあるかを特定し、main
関数内の呼び出し命令をそのアドレスに修正します。 - 再配置 (Relocation): オブジェクトファイルは通常、0番地からの相対アドレスでコードやデータを生成します。リンカは、これらの相対アドレスを最終的な実行可能ファイル内の絶対アドレスに変換し、必要に応じてコード内のアドレス参照を修正します。
- セクション結合 (Section Merging): オブジェクトファイル内のコードセクション(
.text
)、データセクション(.data
)、BSSセクション(.bss
)などを結合し、最終的な実行可能ファイルのメモリレイアウトを決定します。
Go言語のリンカ (cmd/5l
, cmd/6l
, cmd/8l
など)
Go言語のツールチェインには、各アーキテクチャ(ARM, AMD64, x86など)に対応する専用のリンカが含まれています。
5l
: ARMアーキテクチャ向けリンカ6l
: AMD64アーキテクチャ向けリンカ8l
: x86アーキテクチャ向けリンカ
これらのリンカは、Goのランタイムや標準ライブラリ、ユーザーコードをリンクするために使用されます。Goのリンカは、C言語のリンカ(ld
など)とは異なり、Goの特定の機能(例: goroutineのスタック管理、インターフェースのディスパッチなど)をサポートするために、よりGoランタイムと密接に連携しています。
patch()
関数
リンカにおけるpatch()
関数は、一般的に、シンボル解決や再配置の過程で、生成されるバイナリコード内の特定のアドレスや命令を修正(パッチ適用)するために使用されます。これは、例えば、関数呼び出しのターゲットアドレスを埋め込んだり、グローバル変数への参照を修正したりする際に必要となります。
シンボル (Sym
) 構造体と text
, value
フィールド
リンカ内部では、プログラム内の各シンボル(関数、グローバル変数など)が内部的なデータ構造(例えばSym
構造体)で表現されます。この構造体には、シンボルの名前、型、サイズ、そしてメモリ上の位置に関する情報が含まれます。
s->text
: このフィールドは、シンボルがコードセクション(.text
)に属する関数などの場合、そのシンボルの実体(コードブロック)へのポインタ、またはその実体がメモリ上でどこに配置されるかを示す情報を持つことがあります。nil
でない場合は、シンボルの実体が既に確定していることを示唆します。s->value
: このフィールドは、シンボルの最終的なアドレス(オフセット)を表します。リンカがシンボル解決と再配置を完了した後に、この値が確定します。
このコミットの文脈では、s->text
がnil
でない場合、それはシンボルが既に処理され、そのコードやデータがメモリ上のどこに配置されるかが確定していることを意味します。したがって、s->value
を改めて検索する必要がない、というロジックが適用されます。
技術的詳細
このコミットは、src/cmd/5l/pass.c
ファイル内のpatch()
関数を修正しています。patch()
関数は、リンカのパスの一部として、生成されるバイナリコード内の参照を修正する役割を担っています。
変更の核心は、条件分岐命令(ABL
, ABX
, AB
, ARET
)のターゲットシンボルを処理するロジックにあります。これらの命令は、通常、関数呼び出しやジャンプなど、プログラムの制御フローを変更するために使用されます。
元のコードでは、これらの命令のターゲットがシンボル(p->to.sym
)であり、それがS
(おそらく未定義シンボルを示す特別なシンボル)でない場合に、シンボルs
を取得し、その型に応じて処理を行っていました。特にSTEXT
(テキストセクションのシンボル、つまり関数)の場合、p->to.offset
にs->value
を設定し、p->to.type
をD_BRANCH
に設定していました。
今回の最適化は、このSTEXT
ケースの処理を改善しています。
-
if(s->text == nil) continue;
の追加:patch()
関数がシンボルs
を処理する際、まずs->text
がnil
であるかどうかをチェックします。もしs->text
がnil
であれば、そのシンボルはまだ完全に解決されていないか、その実体が確定していない状態であると判断し、現在の処理をスキップして次の命令のパッチ適用に進みます(continue
)。これにより、未確定なシンボルに対して不要な処理を試みることを避けます。これは、リンカの複数パス処理において、シンボルが完全に解決される前にpatch()
が呼び出される可能性がある場合に特に有効です。 -
STEXT
ケースの変更:p->to.offset = s->value;
はそのまま残ります。これは、シンボルの最終的なアドレスオフセットを命令のターゲットオフセットに設定する重要なステップです。p->to.type = D_BRANCH;
もそのまま残ります。これは、ターゲットが分岐であることを示します。p->cond = s->text;
の追加: ここが重要な変更点です。p->cond
フィールドは、命令の条件コードや、場合によっては分岐ターゲットの追加情報(例えば、シンボルの実体へのポインタ)を保持するために使用されることがあります。s->text
をp->cond
に設定することで、シンボルの実体への直接的な参照を命令構造に埋め込むことができます。これにより、後続の処理でs->value
を再検索する手間を省くことができます。break;
をcontinue;
に変更: 元のコードではSTEXT
ケースの処理後にswitch
文をbreak
していましたが、continue
に変更されています。これは、STEXT
ケースの処理が完了したら、現在の命令のパッチ適用は完了し、次の命令の処理に進むべきであることを示唆しています。
この変更により、s->text
が既に利用可能な場合、リンカはシンボルのvalue
を再検索する代わりに、s->text
を直接利用して効率的にパッチ適用を行うことができます。これは、特に大きなプログラムや多数のシンボルを持つプログラムにおいて、リンカの実行時間を短縮する効果が期待できます。
コアとなるコードの変更箇所
--- a/src/cmd/5l/pass.c
+++ b/src/cmd/5l/pass.c
@@ -213,6 +213,8 @@ patch(void)
if((a == ABL || a == ABX || a == AB || a == ARET) &&
p->to.type != D_BRANCH && p->to.sym != S) {
s = p->to.sym;
+ if(s->text == nil)
+ continue;
switch(s->type) {
default:
diag("undefined: %s", s->name);
@@ -222,7 +224,8 @@ patch(void)
case STEXT:
p->to.offset = s->value;
p->to.type = D_BRANCH;
- break;
+ p->cond = s->text;
+ continue;
}
}
if(p->to.type != D_BRANCH)
コアとなるコードの解説
変更はsrc/cmd/5l/pass.c
ファイルのpatch()
関数内で行われています。
-
追加された行 (
+
で始まる行):+ if(s->text == nil) + continue;
このコードは、
ABL
,ABX
,AB
,ARET
といった分岐命令のターゲットシンボルs
を処理する直前に追加されました。もしシンボルs
のtext
フィールドがnil
(ヌルポインタ)であれば、そのシンボルはまだ実体が確定していないか、処理の準備ができていないと判断し、現在の命令のパッチ適用をスキップして次の命令に移ります。これにより、未確定なシンボルに対する無駄な処理を回避し、リンカの効率を向上させます。 -
変更された行 (
-
と+
で始まる行):- break; + p->cond = s->text; + continue;
これは
switch(s->type)
文のcase STEXT:
ブロック内の変更です。p->to.offset = s->value;
とp->to.type = D_BRANCH;
はそのままです。これらは、分岐命令のターゲットオフセットとタイプを設定する基本的な処理です。p->cond = s->text;
が追加されました。これは、命令構造体p
のcond
フィールドに、シンボルs
のtext
フィールドの値を代入しています。s->text
は、シンボルの実体(コードブロック)へのポインタや、その実体がメモリ上のどこに配置されるかを示す情報を持つため、これをp->cond
に設定することで、後続の処理でシンボルの実体への直接的な参照を利用できるようになります。これにより、s->value
を再検索する手間を省き、処理を高速化します。break;
がcontinue;
に変更されました。break
はswitch
文を抜けるだけですが、continue
は現在のfor
ループ(patch
関数内の命令をイテレートするループ)の次のイテレーションに進みます。これは、STEXT
ケースの処理が完了したら、現在の命令のパッチ適用は完了し、次の命令の処理に進むべきであることを明確に示しています。
これらの変更により、patch()
関数は、シンボルのtext
フィールドが既に利用可能な場合に、より効率的にシンボル参照を解決し、命令にパッチを適用できるようになります。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のツールチェインに関するドキュメント (Goのリンカの動作についてより深く理解するための出発点): https://go.dev/doc/
- Goのリンカのソースコード (
src/cmd/link
): https://github.com/golang/go/tree/master/src/cmd/link - GoのARMアーキテクチャ向けリンカ (
src/cmd/5l
): https://github.com/golang/go/tree/master/src/cmd/5l
参考にした情報源リンク
- Go言語のコミットメッセージとdiff:
/home/orange/Project/comemo/commit_data/11509.txt
- Go言語のリンカに関する一般的な情報 (Web検索結果に基づく)
- Goのリンカの内部構造や動作に関するブログ記事やドキュメント (例: "Go linker internals", "Go toolchain")
- コンパイラとリンカの基本的な概念に関するコンピュータサイエンスの教科書やオンラインリソース
- Go CL 5601046: https://golang.org/cl/5601046 (このコミットの元のコードレビューページ)
- Goのソースコード (
src/cmd/5l/pass.c
の関連部分)