[インデックス 16502] ファイルの概要
このコミットは、Go言語のリンカである cmd/6l
の optab.c
ファイルに対する変更です。optab.c
は、Goコンパイラおよびリンカが使用する命令のオペランド型定義テーブルを格納しています。具体的には、各命令がどのような種類のオペランド(レジスタ、メモリ、即値など)を取ることができるかを定義しています。
コミット
commit 26d43a0f22e4d19350bd5243253ce76018651861
Author: Russ Cox <rsc@golang.org>
Date: Wed Jun 5 10:38:52 2013 -0400
cmd/6l: accept NOP of $x+10(SP) and of X0
Needed to link code compiled with 6c -N.
R=ken2
CC=golang-dev
https://golang.org/cl/10043044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/26d43a0f22e4d19350bd5243253ce76018651861
元コミット内容
cmd/6l
: $x+10(SP)
および X0
の NOP
を受け入れるようにする。
6c -N
でコンパイルされたコードをリンクするために必要。
変更の背景
この変更の背景には、Goコンパイラ 6c
の特定のコンパイルオプション -N
が関係しています。6c -N
オプションは、Goの標準ライブラリの一部をコンパイルする際に使用されることがあり、通常はデバッグ情報や最適化を抑制するために使われます。このオプションが有効な場合、コンパイラは特定の形式の NOP
(No Operation) 命令を生成することがありました。
しかし、当時のリンカ 6l
は、これらの特定の形式の NOP
命令(具体的には、スタックポインタからのオフセットを持つアドレス指定 $x+10(SP)
や、特定のレジスタ X0
をオペランドとする NOP
)を正しく認識し、処理することができませんでした。その結果、6c -N
でコンパイルされたコードをリンクしようとすると、リンカエラーが発生し、実行可能ファイルを生成できない問題がありました。
このコミットは、リンカ 6l
がこれらの新しい NOP
オペランドの形式を認識し、適切に処理できるようにすることで、6c -N
でコンパイルされたコードのリンクを可能にすることを目的としています。
前提知識の解説
Goコンパイラ (6c
) とリンカ (6l
)
6c
: Go言語のソースコードをアセンブリコード(またはオブジェクトファイル)にコンパイルするツールです。Goのツールチェーンの一部であり、go build
コマンドの内部で利用されます。6c
の6
は、当時主流だったAMD64アーキテクチャ(x86-64)を指します。6l
:6c
によって生成されたオブジェクトファイルや、他のライブラリを結合して実行可能なバイナリを生成するリンカです。リンカは、異なるオブジェクトファイル間の参照を解決し、最終的な実行可能ファイルを構築します。
NOP
(No Operation) 命令
NOP
は「No Operation」の略で、CPUに対して何もしないことを指示する命令です。通常、プログラムの実行フローには影響を与えませんが、以下のような目的で使用されます。
- アライメント: コードのアライメントを調整し、パフォーマンスを向上させるため。
- パディング: 命令のサイズを調整するため。
- デバッグ: ブレークポイントを設定したり、コードの実行を一時的に停止させたりするため。
- コードの置き換え: 将来的に命令を挿入するためのプレースホルダーとして。
アセンブリ表記 $x+10(SP)
と X0
Goのアセンブリ言語は、Plan 9アセンブラの構文に基づいています。
SP
(Stack Pointer): スタックの現在のトップを指すレジスタです。$x+10(SP)
: これは「スタックポインタSP
が指すアドレスからx+10
バイトオフセットしたメモリ位置」を意味します。$
は即値ではなく、アドレス指定であることを示唆しています。Goのアセンブリでは、$offset(SP)
の形式でスタック上のローカル変数や引数にアクセスします。この場合、x
はシンボルまたは定数で、10
はオフセットです。X0
: これは特定のレジスタを指します。Goのアセンブリでは、AX
,BX
,CX
,DX
などの汎用レジスタの他に、X0
,X1
などの浮動小数点レジスタやSIMDレジスタが使用されることがあります。この文脈では、X0
は特定のレジスタ(おそらく浮動小数点レジスタまたはSIMDレジスタ)を指していると考えられます。
optab.c
とオペランド型定義
optab.c
ファイルは、Goのコンパイラおよびリンカが命令を処理するために必要なオペランドの型定義テーブルを含んでいます。このテーブルは、各命令がどのような種類のオペランド(レジスタ、メモリ、即値など)を取ることができるかをリンカに伝えます。
ynop
テーブル:NOP
命令のオペランドの組み合わせを定義するテーブルです。Ynone
: オペランドがないことを示します。Yml
: メモリ参照(Memory Location)オペランドを示します。Yrf
: 汎用レジスタ(Register File)オペランドを示します。Yiauto
: スタック上の自動変数($offset(SP)
の形式)を示すオペランドです。Yxr
: 拡張レジスタ(eXtended Register)、例えば浮動小数点レジスタやSIMDレジスタを示すオペランドです。Zpseudo
: 疑似命令(リンカが特別に処理する命令)であることを示します。- 最後の数値 (0または1): これは、そのオペランドの組み合わせが命令のサイズに影響を与えるかどうかを示すフラグである可能性があります。
0
はサイズに影響しない、1
は影響する、といった意味合いが考えられます。
技術的詳細
このコミットの技術的な核心は、リンカ 6l
が NOP
命令の新しいオペランド形式を正しく解釈できるように、optab.c
内の ynop
テーブルを拡張することにあります。
Goのコンパイラ 6c
は、特定の状況下(特に -N
オプションが有効な場合)で、NOP
命令にスタックオフセット付きのアドレス指定($x+10(SP)
)や、特定のレジスタ(X0
)をオペランドとして付加した形式を生成するようになりました。
リンカは、オブジェクトファイルを読み込む際に、各命令とそのオペランドの型を認識し、それに基づいて命令のサイズを計算したり、アドレスを解決したりします。optab.c
の ynop
テーブルは、NOP
命令がどのようなオペランドの組み合わせを許容するかをリンカに指示します。
変更前は、ynop
テーブルには Yiauto
(スタック上の自動変数) や Yxr
(拡張レジスタ) をオペランドとする NOP
のエントリがありませんでした。そのため、6c -N
が生成するこれらの形式の NOP
命令に遭遇すると、リンカはそれを未知の命令形式と判断し、エラーを報告していました。
このコミットでは、ynop
テーブルに以下の新しいエントリが追加されました。
Ynone, Yiauto, Zpseudo, 0,
: オペランドなしとスタック上の自動変数 ($x+10(SP)
に対応) の組み合わせ。Ynone, Yxr, Zpseudo, 0,
: オペランドなしと拡張レジスタ (X0
に対応) の組み合わせ。Yiauto, Ynone, Zpseudo, 0,
: スタック上の自動変数とオペランドなしの組み合わせ。Yxr, Ynone, Zpseudo, 1,
: 拡張レジスタとオペランドなしの組み合わせ。
これらのエントリを追加することで、リンカは $x+10(SP)
や X0
をオペランドとする NOP
命令を、有効な命令形式として認識できるようになります。これにより、6c -N
でコンパイルされたコードがリンカによって正常に処理され、実行可能ファイルが生成されるようになります。
特に注目すべきは、Yxr, Ynone, Zpseudo, 1,
の最後の値が 1
である点です。これは、この特定の NOP
形式が命令のサイズに影響を与える可能性があることを示唆しています。他の新しいエントリが 0
であることから、この Yxr
を含む NOP
は、他の NOP
とは異なるサイズを持つ可能性があるか、またはリンカが特別な処理を行う必要があることを示しています。
コアとなるコードの変更箇所
src/cmd/6l/optab.c
ファイルの ynop
配列が変更されています。
--- a/src/cmd/6l/optab.c
+++ b/src/cmd/6l/optab.c
@@ -42,11 +42,15 @@ uchar ytext[] =
};
uchar ynop[] =
{
- Ynone, Ynone, Zpseudo,1,
- Ynone, Yml,\tZpseudo,1,
- Ynone, Yrf,\tZpseudo,1,
- Yml, Ynone,\tZpseudo,1,
- Yrf, Ynone,\tZpseudo,1,
+ Ynone, Ynone, Zpseudo,0,
+ Ynone, Yiauto, Zpseudo,0,
+ Ynone, Yml, Zpseudo,0,
+ Ynone, Yrf, Zpseudo,0,
+ Ynone, Yxr, Zpseudo,0,
+ Yiauto, Ynone, Zpseudo,0,
+ Yml, Ynone, Zpseudo,0,
+ Yrf, Ynone, Zpseudo,0,
+ Yxr, Ynone, Zpseudo,1,
0
};
uchar yxorb[] =
コアとなるコードの解説
変更は ynop
配列内で行われています。この配列は、NOP
命令が取りうるオペランドの組み合わせを定義しています。各行は4つの要素からなるタプルで、それぞれ「ソースオペランドの型」「デスティネーションオペランドの型」「命令の種類(疑似命令など)」「フラグ」を表しています。
元のコードでは、NOP
は主に Ynone
(オペランドなし)、Yml
(メモリ参照)、Yrf
(汎用レジスタ) の組み合わせしかサポートしていませんでした。
追加された行は以下の通りです。
Ynone, Yiauto, Zpseudo, 0,
- これは、ソースオペランドなし (
Ynone
) で、デスティネーションオペランドがスタック上の自動変数 (Yiauto
、つまり$x+10(SP)
の形式) であるNOP
をリンカが認識できるようにします。
- これは、ソースオペランドなし (
Ynone, Yxr, Zpseudo, 0,
- これは、ソースオペランドなし (
Ynone
) で、デスティネーションオペランドが拡張レジスタ (Yxr
、つまりX0
の形式) であるNOP
をリンカが認識できるようにします。
- これは、ソースオペランドなし (
Yiauto, Ynone, Zpseudo, 0,
- これは、ソースオペランドがスタック上の自動変数 (
Yiauto
) で、デスティネーションオペランドなし (Ynone
) であるNOP
をリンカが認識できるようにします。
- これは、ソースオペランドがスタック上の自動変数 (
Yxr, Ynone, Zpseudo, 1,
- これは、ソースオペランドが拡張レジスタ (
Yxr
) で、デスティネーションオペランドなし (Ynone
) であるNOP
をリンカが認識できるようにします。最後の1
は、この特定の組み合わせが命令のサイズに影響を与える可能性があることを示唆しています。
- これは、ソースオペランドが拡張レジスタ (
また、既存のいくつかのエントリの最後のフラグが 1
から 0
に変更されています。これは、これらの NOP
形式が命令のサイズに影響を与えないことを明示している可能性があります。
これらの変更により、6c -N
コンパイラが生成する特定の NOP
命令形式がリンカによって正しく解釈され、リンクプロセスが正常に完了するようになります。
関連リンク
- Go Code Review: https://golang.org/cl/10043044
参考にした情報源リンク
- Go Assembly Language (Plan 9 style): https://go.dev/doc/asm
- Go Toolchain Documentation (for
6c
,6l
context): https://go.dev/doc/cmd - Understanding NOP instructions: General computer architecture knowledge.
- Stack Pointer (SP) and addressing modes: General assembly language knowledge.
- Go source code for
cmd/6l/optab.c
(for context onYnone
,Yml
,Yrf
,Zpseudo
etc.): https://github.com/golang/go/blob/master/src/cmd/6l/optab.c (Note: This link points to the current master, not the exact historical version, but provides general context.) - Discussion on
6c -N
(if available, often found in Go issue tracker or mailing lists).